From 9c0916f7113ec5f70c13c9dbdc61b8fc1d819003 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 13:39:16 +0530 Subject: [PATCH 01/37] chore(quality): repair the knip gate and drop unused dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit knip:strict runs in CI (ci.yml "Code health") but was analyzing zero files: knip.json declared root-relative entry paths while pnpm-workspace.yaml sets packages: apps/*, so knip resolved the apps as separate workspaces and every entry/project pattern matched nothing. `knip --reporter json` returned {"issues":[]}. The unused-code gate has been passing vacuously. Rewrite the config workspace-aware (., apps/desktop, apps/landing-page-astro) and strip the suppression that hid real findings — ignoreDependencies drops from ~24 entries to 2, and the blanket ignore list is gone entirely. With the gate live it reported six genuinely unused dependencies, verified by hand as having zero imports anywhere in the desktop app. They were invisible because the old config named @xterm/* and react-resizable-panels in ignoreDependencies. They are leftovers from the removed agent terminal UI. ignoreIssues now whitelists the warm-verification / scenario-compiler contract modules file-by-file rather than by directory glob, so a new dead module in those directories still fails the gate. Verified by canary: adding an unused export is detected. Co-Authored-By: Claude Opus 5 (1M context) --- apps/desktop/package.json | 6 - knip.json | 301 ++++++++++++++++++++------------------ pnpm-lock.yaml | 58 -------- 3 files changed, 159 insertions(+), 206 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index ea9487df..b04aa7de 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -65,17 +65,11 @@ "@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-process": "^2.0.0", "@tauri-apps/plugin-updater": "^2.0.0", - "@xterm/addon-fit": "^0.10.0", - "@xterm/addon-search": "0.16.0", - "@xterm/addon-web-links": "^0.12.0", - "@xterm/addon-webgl": "0.19.0", - "@xterm/xterm": "^5.5.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.7.0", "react": "^19.2.8", "react-dom": "^19.2.8", - "react-resizable-panels": "^4.9.0", "react-router-dom": "^7.18.2", "tailwind-merge": "^3.5.0" }, diff --git a/knip.json b/knip.json index d39c60b3..61c94e41 100644 --- a/knip.json +++ b/knip.json @@ -1,157 +1,174 @@ { "$schema": "https://unpkg.com/knip@latest/schema.json", - "entry": [ - "apps/desktop/src/main.tsx", - "apps/desktop/src/App.tsx", - "apps/desktop/src/lib/verify-cli.ts", - "apps/desktop/src/lib/warm-verification/daemon-entry.ts", - "apps/desktop/src/lib/intent-debugger/run-intent-cli.ts", - "apps/desktop/src/lib/synthetic-qa/run-fixture-cli.ts" - ], - "project": ["**/*.{ts,tsx,js,jsx,mjs,cjs,mts}"], - "ignore": [ - "**/dist/**", - "**/build/**", - "**/.next/**", - "**/.astro/**", - "**/.output/**", - "**/node_modules/**", - "**/coverage/**", - "**/public/**", - "**/*.config.{js,ts,mjs,cjs}", - "**/eslint.config.*", - "**/blume.config.*", - "**/docs-blume/**", - "**/docs-site/**", - "scripts/**", - "**/scripts/**", - "tests/e2e/**", - "**/tests/**", - "test/**", - "**/*.test.{ts,tsx,js,mjs}", - "**/*.spec.{ts,tsx,js,mjs}", - "**/*.d.mts", - "**/*.d.ts", - "**/__tests__/**", - "**/playground/**", - "**/examples/**", - "**/fixtures/**", - "**/verify/**", - "benchmark/**", - "benchmarks/**", - "openspec/**", - "research/**", - "**/worker.mjs", - "**/src-tauri/**" - ], + "workspaces": { + ".": { + "entry": [ + "scripts/**/*.mjs" + ], + "project": [ + "scripts/**/*.mjs" + ] + }, + "apps/desktop": { + "entry": [ + "scripts/**/*.mjs", + "tests/**/*.spec.ts", + "src/**/*.test.{ts,tsx}" + ], + "project": [ + "src/**/*.{ts,tsx}", + "scripts/**/*.mjs" + ] + }, + "apps/landing-page-astro": { + "entry": [ + "src/pages/**/*.{astro,ts}" + ], + "project": [ + "src/**/*.{ts,astro}" + ] + } + }, "ignoreDependencies": [ - "typescript", - "@types/*", - "biome", - "@biomejs/biome", - "prettier", - "prettier-plugin-tailwindcss", - "husky", - "lint-staged", - "vitest", - "@vitest/*", - "playwright", - "@playwright/test", "tailwindcss", - "@tailwindcss/*", - "lightningcss", - "tsx", - "size-limit", - "knip", - "blume", - "@blume/*", - "astro", - "@astrojs/*", - "@shikijs/*", - "shiki", - "wrangler", - "@tauri-apps/cli", - "@tauri-apps/api", - "@tauri-apps/plugin-*", - "@vitejs/plugin-react", - "vite", - "autoprefixer", - "postcss", - "c8", - "msw", - "yaml", - "class-variance-authority", - "clsx", - "tailwind-merge", - "tailwindcss-animate", - "@radix-ui/*", - "lucide-react", - "react", - "react-dom", - "react-router-dom", - "react-resizable-panels", - "@xterm/*", - "@axe-core/playwright", "ccusage" ], "ignoreBinaries": [ - "astro", - "vite", - "wrangler", - "tsx", - "biome", - "playwright", - "knip", - "prettier", - "blume", - "tauri", + "du", + "go", + "lipo", "lsof", "netstat", "ps", "rustc", - "cargo", - "swift" + "swift", + "tauri", + "xcode-select" ], "ignoreIssues": { - "src/app/api/**/route.ts": ["exports", "types"], - "src/lib/actions/**": ["exports", "types"], - "lib/actions/**": ["exports", "types"], - "**/schema.ts": ["exports", "types"], - "**/db/schema*.ts": ["exports", "types"], - "**/commands/**": ["exports", "types"], - "**/agent-edge.*": ["exports", "types"], - "**/foundry-monitoring.ts": ["exports", "types"], - "**/analytics.ts": ["exports", "types"], - "**/api-timing.ts": ["exports", "types"], - "**/contracts.ts": ["exports", "types"], - "**/differential-contracts.ts": ["exports", "types"], - "**/differential-daemon-contracts.ts": ["exports", "types"], - "**/differential-plan.ts": ["exports", "types"], - "**/differential-config.ts": ["exports", "types"], - "**/differential-source.ts": ["exports", "types"], - "**/differential-comparator.ts": ["exports", "types", "duplicates"], - "**/differential-timing-policy.ts": ["exports", "types"], - "**/differential-scheduler.ts": ["exports", "types"], - "**/qualification-policy.ts": ["exports", "types"], - "**/tauri-ipc.ts": ["exports", "types"], - "**/verification-state-bridge.ts": ["exports", "types"], - "**/warm-verification/config.ts": ["exports", "types"], - "**/warm-verification/config-loader.ts": ["exports", "types"], - "**/warm-verification/contracts.ts": ["exports", "types"], - "**/warm-verification/adapters.ts": ["exports", "types"], - "**/warm-verification/change-set.ts": ["exports", "types"], - "**/warm-verification/ipc.ts": ["exports", "types"], - "**/warm-verification/manifest-loader.ts": ["exports", "types"], - "**/warm-verification/scenario.ts": ["exports", "types"], - "**/warm-verification/source-watcher.ts": ["exports", "types"], - "**/warm-verification/state.ts": ["exports", "types"], - "**/warm-verification/supervision.ts": ["exports", "types"], - "**/warm-verification/visual.ts": ["exports", "types"], - "**/warm-verification/selection.ts": ["exports", "types"], - "**/warm-verification/retention.ts": ["exports", "types"], - "**/scenario-compiler/contracts.ts": ["exports", "types"], - "**/scenario-compiler/candidate.ts": ["exports", "types"], - "**/scenario-compiler/cli.ts": ["exports", "types"], - "**/scenario-compiler/provider.ts": ["exports", "types"] + "apps/desktop/src/lib/business-rule-archaeology/qualification-policy.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/scenario-compiler/candidate.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/scenario-compiler/cli.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/scenario-compiler/provider.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/verification-state-bridge.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/adapters.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/change-set.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/config-loader.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/config.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-comparator.ts": [ + "duplicates", + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-config.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-contracts.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-daemon-contracts.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-plan.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-scheduler.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-source.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/differential-timing-policy.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/ipc.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/manifest-loader.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/retention.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/scenario.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/selection.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/source-watcher.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/state.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/supervision.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/visual.ts": [ + "exports", + "types" + ], + "**/commands/**": [ + "exports", + "types" + ], + "**/tauri-ipc.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/warm-verification/contracts.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/business-rule-archaeology/contracts.ts": [ + "exports", + "types" + ], + "apps/desktop/src/lib/scenario-compiler/contracts.ts": [ + "types" + ] } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8c0b76f..ec4de4e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -81,21 +81,6 @@ importers: '@tauri-apps/plugin-updater': specifier: ^2.0.0 version: 2.10.1 - '@xterm/addon-fit': - specifier: ^0.10.0 - version: 0.10.0(@xterm/xterm@5.5.0) - '@xterm/addon-search': - specifier: 0.16.0 - version: 0.16.0 - '@xterm/addon-web-links': - specifier: ^0.12.0 - version: 0.12.0 - '@xterm/addon-webgl': - specifier: 0.19.0 - version: 0.19.0 - '@xterm/xterm': - specifier: ^5.5.0 - version: 5.5.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -111,9 +96,6 @@ importers: react-dom: specifier: ^19.2.8 version: 19.2.8(react@19.2.8) - react-resizable-panels: - specifier: ^4.9.0 - version: 4.10.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-router-dom: specifier: ^7.18.2 version: 7.18.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -2307,23 +2289,6 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - '@xterm/addon-fit@0.10.0': - resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==} - peerDependencies: - '@xterm/xterm': ^5.0.0 - - '@xterm/addon-search@0.16.0': - resolution: {integrity: sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==} - - '@xterm/addon-web-links@0.12.0': - resolution: {integrity: sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==} - - '@xterm/addon-webgl@0.19.0': - resolution: {integrity: sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A==} - - '@xterm/xterm@5.5.0': - resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} - am-i-vibing@0.4.0: resolution: {integrity: sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==} hasBin: true @@ -3569,12 +3534,6 @@ packages: '@types/react': optional: true - react-resizable-panels@4.10.0: - resolution: {integrity: sha512-frjewRQt7TCv/vCH1pJfjZ7RxAhr5pKuqVQtVgzFq/vherxBFOWyC3xMbryx5Ti2wylViGUFc93Etg4rB3E0UA==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - react-router-dom@7.18.2: resolution: {integrity: sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==} engines: {node: '>=20.0.0'} @@ -5905,18 +5864,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)': - dependencies: - '@xterm/xterm': 5.5.0 - - '@xterm/addon-search@0.16.0': {} - - '@xterm/addon-web-links@0.12.0': {} - - '@xterm/addon-webgl@0.19.0': {} - - '@xterm/xterm@5.5.0': {} - am-i-vibing@0.4.0: dependencies: process-ancestry: 0.1.0 @@ -7218,11 +7165,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.18 - react-resizable-panels@4.10.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-router-dom@7.18.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: react: 19.2.8 From f50bd728315007545e12b8f3247ef640aacb7f33 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 13:40:08 +0530 Subject: [PATCH 02/37] chore(repo): untrack orphaned tool output and tidy gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .impeccable/ and .claude/projects/ had zero inbound references anywhere in the repo — they are local tool output, not shared repo content. Untrack them (kept on disk) and gitignore them. .claude/settings.local.json was tracked despite already being listed in .gitignore. Its contents are stale per-developer state: every path points at /Users/sarthakagrawal/Desktop/code-reviewer/ with a packages/ + workers/ layout that no longer exists, plus Vercel permissions from before the move to Cloudflare Pages. research/ held one real document — a survey of codebase-context tooling — so move it to docs/knowledge/ with the frontmatter check-docs.mjs requires rather than dropping it. Also drop gitignore entries for a layout that no longer exists: the "Action runtime bundle" dist/* + !dist/index.js pair (there is no action.yml in this repo), packages/*, workers/* and .vercel. dist/* becomes dist/ rather than being deleted, since it matches at any depth and is what keeps docs-site/dist and apps/landing-page-astro/dist out of the index. This commit also carries the gitignore lines for the corpus and evidence moves in the two commits that follow, since they share one file. Co-Authored-By: Claude Opus 5 (1M context) --- .../memory/MEMORY.md | 5 - .../memory/project_agent_first_pivot.md | 21 -- .../project_shelved_playwright_testing.md | 19 -- .claude/settings.local.json | 98 -------- .gitignore | 20 +- ...s-run-structural-context-evaluation-mjs.md | 103 --------- ...apps-desktop-src-components-sidebar-tsx.md | 102 --------- ...-01T17-55-01Z__apps-desktop-src-app-tsx.md | 171 -------------- ...31-57Z__apps-desktop-src-pages-home-tsx.md | 74 ------ ...p-src-components-app-error-boundary-tsx.md | 57 ----- ..._apps-desktop-src-pages-performance-tsx.md | 30 --- .impeccable/design.json | 212 ------------------ .../codebase-context-tools-landscape.md | 7 + 13 files changed, 17 insertions(+), 902 deletions(-) delete mode 100644 .claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/MEMORY.md delete mode 100644 .claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_agent_first_pivot.md delete mode 100644 .claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_shelved_playwright_testing.md delete mode 100644 .claude/settings.local.json delete mode 100644 .impeccable/critique/2026-07-27T05-06-55Z__scripts-run-structural-context-evaluation-mjs.md delete mode 100644 .impeccable/critique/2026-07-30T06-34-52Z__apps-desktop-src-components-sidebar-tsx.md delete mode 100644 .impeccable/critique/2026-08-01T17-55-01Z__apps-desktop-src-app-tsx.md delete mode 100644 .impeccable/critique/2026-08-10T18-31-57Z__apps-desktop-src-pages-home-tsx.md delete mode 100644 .impeccable/critique/2026-08-15T20-33-20Z__apps-desktop-src-components-app-error-boundary-tsx.md delete mode 100644 .impeccable/critique/2026-08-15T21-39-48Z__apps-desktop-src-pages-performance-tsx.md delete mode 100644 .impeccable/design.json rename {research => docs/knowledge}/codebase-context-tools-landscape.md (99%) diff --git a/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/MEMORY.md b/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/MEMORY.md deleted file mode 100644 index 4ec74f63..00000000 --- a/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/MEMORY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Memory Index - -## Project Direction -- [Agent-First Pivot](project_agent_first_pivot.md) — CodeVetter pivoting to AI-agent-first: reviewing agent-generated PRs, agent-to-agent interaction on GitHub -- [Shelved: Playwright Testing](project_shelved_playwright_testing.md) — AI E2E testing idea explored and shelved, possible v2+ "verify the fix" feature diff --git a/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_agent_first_pivot.md b/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_agent_first_pivot.md deleted file mode 100644 index 379792fd..00000000 --- a/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_agent_first_pivot.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Product Pivot - AI Agent-First Code Review -description: CodeVetter is pivoting to be an AI-agent-first product — reviewing PRs generated by AI coding agents and interacting with agents directly on GitHub PR pages. -type: project ---- - -## Product Direction: AI Agent-First (2026-03-11) - -**Core pivot:** CodeVetter should be an AI-agent-first product, not human-first. - -**What this means:** -- Primary audience: AI coding agents (Claude Code, Cursor, Devin, Copilot Workspace, etc.) that generate PRs -- CodeVetter reviews all PRs generated by agents automatically -- AI agents can talk to CodeVetter directly on the GitHub PR page itself (agent-to-agent conversation) -- The product is a quality gate / reviewer for AI-generated code - -**Why this matters:** -- AI agents are generating an increasing volume of PRs -- Human reviewers can't keep up with the volume -- Agent-generated code has different failure modes than human code (hallucinated APIs, subtle logic errors, over-engineering) -- Agent-to-agent review loop on GitHub is a novel interaction model diff --git a/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_shelved_playwright_testing.md b/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_shelved_playwright_testing.md deleted file mode 100644 index 05113298..00000000 --- a/.claude/projects/-Users-sarthakagrawal-Desktop-code-reviewer/memory/project_shelved_playwright_testing.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Shelved - AI Playwright Testing Feature -description: Idea to add AI-driven Playwright E2E testing was explored and shelved. Could revisit as a "verify the fix" feature in v2+. -type: project ---- - -## Shelved Idea: AI-Powered Playwright Testing (2026-03-11) - -**Concept:** Users provide auth + instructions, AI uses Playwright to run E2E tests on their app. - -**Decision:** Shelved. Not pursuing now. - -**Reasons:** -- Market already crowded (Browserbase, Momentic, QA Wolf, Shortest, Drizzle AI) -- Tangential to core code review value prop -- Expensive to run on every PR; running only before releases makes it an afterthought -- Would split focus from core product - -**Potential future angle:** "Verify the fix" step triggered by review intelligence — differentiated because it's driven by CodeVetter's review findings, not generic E2E. Only revisit if users explicitly ask for it. diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 9ffcd90a..00000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(git add:*)", - "Bash(tail:*)", - "Bash(which npm:*)", - "Bash(git:*)", - "Bash(which tsc:*)", - "Bash(/Users/sarthakagrawal/Desktop/code-reviewer/node_modules/.bin/tsc:*)", - "Bash(head:*)", - "Bash(node:*)", - "Bash(pkill:*)", - "Bash(sleep 2:*)", - "Bash(sleep 3:*)", - "mcp__plugin_playwright_playwright__browser_navigate", - "Bash(whois:*)", - "Bash(which vercel 2>/dev/null && vercel --version 2>/dev/null; which wrangler 2>/dev/null && wrangler --version 2>/dev/null)", - "Bash(wrangler whoami:*)", - "Bash(vercel teams:*)", - "Bash(wrangler:*)", - "Bash(cd /Users/sarthakagrawal/Desktop/code-reviewer/apps/landing-page && vercel --prod --yes 2>&1)", - "Bash(cd /Users/sarthakagrawal/Desktop/code-reviewer/apps/landing-page && vercel link --yes --scope sarthak-agrawals-projects-5d4953f8 2>&1 && vercel --prod --yes --scope sarthak-agrawals-projects-5d4953f8 2>&1)", - "Bash(vercel project:*)", - "Bash(vercel link:*)", - "Bash(vercel:*)", - "mcp__plugin_playwright_playwright__browser_install", - "mcp__plugin_playwright_playwright__browser_take_screenshot", - "Bash(find:*)", - "Bash(grep:*)", - "Bash(fd:*)", - "Bash(wc:*)", - "Bash(ls:*)", - "Bash(for f:*)", - "Read(//Users/sarthakagrawal/Desktop/code-reviewer/**)", - "Bash(bash /tmp/count_dashboard.sh)", - "Bash(cat:*)", - "Bash(cd:*)", - "Bash(npx tsc:*)", - "Bash(npx vite:*)", - "Bash(npm install:*)", - "Bash(lsof:*)", - "Bash(curl:*)", - "Bash(python3:*)", - "WebSearch", - "WebFetch(domain:www.greptile.com)", - "WebFetch(domain:www.coderabbit.ai)", - "WebFetch(domain:www.conductor.build)", - "WebFetch(domain:www.ellipsis.dev)", - "WebFetch(domain:bito.ai)", - "WebFetch(domain:whatthediff.ai)", - "WebFetch(domain:superset.sh)", - "WebFetch(domain:github.com)", - "WebFetch(domain:news.ycombinator.com)", - "WebFetch(domain:www.ycombinator.com)", - "WebFetch(domain:makerstack.co)", - "WebFetch(domain:launchllama.co)", - "WebFetch(domain:docs.superset.sh)", - "WebFetch(domain:www.producthunt.com)", - "Bash(cargo check:*)", - "WebFetch(domain:docs.dodopayments.com)", - "Bash(pnpm tauri:*)", - "Bash(npx @tauri-apps/cli build)", - "Bash(gh run:*)", - "mcp__claude_ai_Vercel__list_projects", - "mcp__claude_ai_Vercel__get_deployment_build_logs", - "mcp__claude_ai_Vercel__list_deployments", - "Bash(gh release:*)", - "Bash(gh api:*)", - "Bash(xargs -I {} sh -c 'git push origin :refs/tags/{} 2>&1; git tag -d {}')", - "Bash(npm test:*)", - "Bash(npm run:*)", - "Bash(/bin/ls -la /Users/sarthakagrawal/Desktop/code-reviewer/)", - "Bash(/bin/ls -la /Users/sarthakagrawal/Desktop/code-reviewer/apps/)", - "Bash(/bin/ls -la /Users/sarthakagrawal/Desktop/code-reviewer/packages/)", - "Bash(/bin/ls -la /Users/sarthakagrawal/Desktop/code-reviewer/workers/)", - "Bash(/bin/ls -la /Users/sarthakagrawal/Desktop/code-reviewer/tests/)", - "Bash(for pkg:*)", - "Bash(do echo:*)", - "Bash(/usr/bin/python3 -c \"import json; d=json.load\\(open\\(''/Users/sarthakagrawal/Desktop/code-reviewer/$pkg/package.json''\\)\\); scripts=d.get\\(''scripts'',{}\\); [print\\(f'' {k}: {v}''\\) for k,v in scripts.items\\(\\)]\")", - "Bash(done)", - "Bash(/bin/ls /Users/sarthakagrawal/Desktop/code-reviewer/packages/review-core/dist/)", - "Bash(npx eslint:*)", - "Read(//Library/Logs/DiagnosticReports/**)", - "Read(//Users/sarthakagrawal/Library/Logs/DiagnosticReports/**)", - "Bash(pmset -g log)", - "Bash(/bin/ls /Users/sarthakagrawal/Desktop/code-reviewer/packages/db/src/)", - "Bash(sysctl hw.model hw.ncpu kern.version)", - "Bash(/bin/ls /Users/sarthakagrawal/Desktop/code-reviewer/tests/)", - "Bash(ps aux:*)", - "Bash(sudo mdutil:*)", - "Bash(mdutil -s /)", - "Bash(mdutil:*)", - "Read(//Applications/**)", - "Bash(brew list:*)", - "Bash(xargs kill:*)" - ] - } -} diff --git a/.gitignore b/.gitignore index 562233e3..5470fc8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,7 @@ node_modules/ -# Action runtime bundle must stay versioned. -dist/* -!dist/index.js - # Generated build artifacts should not be tracked. -packages/*/dist -workers/*/dist -workers/*/dist-cjs -workers/*/.wrangler -.vercel +dist/ out/ .next/ target/ @@ -35,6 +27,11 @@ apps/desktop/native/AgentIsland/.build/ # Secrets .claude/settings.local.json +# Local agent/tool state and output — not shared repo content. +.claude/projects/ +.fleet/ +.impeccable/ + # IDE .vscode/ .idea/ @@ -50,7 +47,10 @@ apps/desktop/synthetic-qa-artifacts/ .playwright-mcp/ # Public benchmark reviewer output (drop tool outputs here, not tracked) -benchmark/reviews/ +benchmarks/public-catch-rate/reviews/ + +# Scratch output from CLI runs (--out artifacts/...). Committed evidence lives in evidence/. +artifacts/ # Coverage coverage/ diff --git a/.impeccable/critique/2026-07-27T05-06-55Z__scripts-run-structural-context-evaluation-mjs.md b/.impeccable/critique/2026-07-27T05-06-55Z__scripts-run-structural-context-evaluation-mjs.md deleted file mode 100644 index c94c42c3..00000000 --- a/.impeccable/critique/2026-07-27T05-06-55Z__scripts-run-structural-context-evaluation-mjs.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -target: structural-context evaluation HTML report -total_score: 33 -max_score: 40 -na_heuristics: -p0_count: 0 -p1_count: 0 -timestamp: 2026-07-27T05-06-55Z -slug: scripts-run-structural-context-evaluation-mjs ---- -Method: dual-agent (A: impeccable_assessment_a · B: impeccable_assessment_b) - -## Design Health Score - -| # | Heuristic | Score | Key finding | -|---|---|---:|---| -| 1 | Visibility of System Status | 4 | Qualification, pair counts, gates, source, and read-only state are explicit. | -| 2 | Match System / Real World | 3 | A/A and discordance still assume evaluation fluency. | -| 3 | User Control and Freedom | 2 | A static report has no filtering or bulk disclosure controls. | -| 4 | Consistency and Standards | 4 | Evidence hierarchy, arm naming, and CodeVetter tokens are cohesive. | -| 5 | Error Prevention | 4 | Claim boundaries and neutral diagnostic deltas prevent overstatement. | -| 6 | Recognition Rather Than Recall | 4 | Mobile diagnostics now expose every comparison value in each metric card. | -| 7 | Flexibility and Efficiency | 2 | Large experiments will eventually need anchors, filters, or condensed rows. | -| 8 | Aesthetic and Minimalist Design | 4 | The report remains focused and qualification-first across all widths. | -| 9 | Error Recovery | 3 | Invalid pairs explain concrete exclusion reasons; generation errors remain CLI-only. | -| 10 | Help and Documentation | 3 | Inline caveats are strong; evaluation terms have no compact glossary. | -| **Total** | | **33/40** | **Good, above the Fleet floor after polish.** | - -## Design Specificity Verdict - -The report is authored for CodeVetter rather than a generic analytics -dashboard. Its sequence is the product's evidence model: claim boundary, -paired executable outcome, changed checks and graph traces, qualification, -activity diagnostics, and limitations. Amber remains the evidence accent and -cyan is reserved for graph provenance. - -The CLI detector returned zero findings. The rendered detector found 15 -advisory issues before polish: eight small-text or line-length findings and -seven cyan-palette findings. The cyan findings were false positives because the -color has a stable graph-provenance meaning. The small text, touch target, copy -measure, and mobile diagnostics issues were fixed. - -## Overall Impression - -The opening creates curiosity, then immediately constrains interpretation with -an unqualified claim. The paired corridor is the visual peak. The closing -authorized-claim block now restores that same boundary after the evidence -detail, so a long read ends with the correct decision. - -## What's Working - -- Qualification appears before the favorable synthetic percentage. -- Paired outcomes and hidden-check changes are readable without decorative - metric cards. -- Native details, semantic regions, a real data table, textual PASS/FAIL - labels, visible focus, and high contrast support accessible inspection. - -## Priority Issues - -### [P2] Large-run navigation - -The schema permits much larger experiments than the two-pair sample. A future -real corpus may need outcome filters, section anchors, or condensed tie rows. -This does not block the bounded local report. - -### [P3] Evaluation terminology - -A/A, discordance, and coverage are correct but assume statistical fluency. A -compact glossary may help less experienced product owners when real receipts -arrive. - -### [P3] Fixed dark presentation - -The tokenized fixed-dark report is coherent with CodeVetter and includes print -rules, but it does not offer an alternate light screen theme. - -## Persona Red Flags - -**Alex, power user:** The two-pair report is fast to scan, but dozens of pairs -would require outcome filtering and condensed ties. - -**Sam, accessibility-dependent user:** Semantic structure, contrast, keyboard -disclosures, 44px summary targets, and stacked mobile diagnostics now support -the core reading path. A future large corpus needs skip links or section -navigation. - -**Priya, technical product owner:** The synthetic and unqualified boundary is -now tied to both the comparison corridor and the closing verdict. Activity -deltas are neutral and explicitly mean less, not better. - -## Minor Observations - -- Long identities and source paths wrap safely. -- A zero-valid-pair state withholds the percentage corridor. -- Print semantic colors use darker values while retaining text labels. -- Missing optional diagnostics remain missing rather than becoming zero. - -## Questions to Consider - -- At what corpus size should the evidence brief become a navigable - investigation tool? -- Should real-trial reports define a tiny inline glossary for A/A noise and - qualification policy? diff --git a/.impeccable/critique/2026-07-30T06-34-52Z__apps-desktop-src-components-sidebar-tsx.md b/.impeccable/critique/2026-07-30T06-34-52Z__apps-desktop-src-components-sidebar-tsx.md deleted file mode 100644 index 4f4d8232..00000000 --- a/.impeccable/critique/2026-07-30T06-34-52Z__apps-desktop-src-components-sidebar-tsx.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -target: CodeVetter desktop sidebar -total_score: 33 -max_score: 40 -na_heuristics: -p0_count: 0 -p1_count: 0 -timestamp: 2026-07-30T06-34-52Z -slug: apps-desktop-src-components-sidebar-tsx ---- -Method: dual-agent (A: sidebar_critique_a · B: sidebar_critique_b) - -## Design Health Score - -| # | Heuristic | Score | Key issue | -|---|---|---:|---| -| 1 | Visibility of system status | 3 | Active location is clear; the transient G chord remains intentionally quiet. | -| 2 | Match system / real world | 3 | Product labels are established but assume some CodeVetter familiarity. | -| 3 | User control and freedom | 4 | Navigation is reversible and the palette now restores focus to its trigger. | -| 4 | Consistency and standards | 4 | Rows, grouping, focus, active state, and spacing follow the Evidence Bench system. | -| 5 | Error prevention | 3 | Shortcut handling protects form controls; contenteditable remains a narrow edge case. | -| 6 | Recognition rather than recall | 3 | Every destination is labeled; detailed descriptions remain in accessible tooltips. | -| 7 | Flexibility and efficiency | 4 | Search, Cmd-K, and G chords provide strong expert acceleration. | -| 8 | Aesthetic and minimalist design | 4 | The rail is calm, compact, and free of decorative feature noise. | -| 9 | Error recovery | 3 | Search dismisses cleanly and restores focus; mistimed G chords remain silent. | -| 10 | Help and documentation | 2 | Tooltips explain destinations, but the rail intentionally carries no dedicated help surface. | -| **Total** | | **33/40** | **Good; no blocking or major issues remain.** | - -## Design Specificity Verdict - -The sidebar is clearly adapted to CodeVetter through its Context and -Verification grouping, Evidence Workbench identity, warm verification accent, -real product routes, resource utility, and keyboard model. Its basic rail -composition is conventional, but the content and state grammar are not a -generic mockup. - -The deterministic scan returned zero findings across `App.tsx`, `sidebar.tsx`, -`ResourceChip.tsx`, and `command-palette.tsx`. Browser evidence confirmed AA -contrast, one accessible active destination, no overflow at supported desktop -sizes, a working Search trigger, and keyboard focus restoration. No reliable -browser overlay was available because the exposed evaluation surface was -read-only; live screenshots, computed styles, geometry, axe, and Playwright -interaction checks were used instead. - -## Overall Impression - -The new rail feels like a quiet native instrument and carries the reference's -search-first hierarchy without importing an unrelated cream visual system. The -main opportunity was finishing keyboard and control-size details, both of which -were corrected during the pass. - -## What's Working - -- The active state uses position, icon treatment, text, and `aria-current`, so - it is legible without color alone. -- Context, Verification, and bottom utilities produce a clear three-part - information hierarchy. -- Cmd-K, visible G chords, and direct search make the compact shell efficient - for repeat users. - -## Priority Issues - -- **[P1, fixed] Command palette dialog naming:** Opening Search exposed a Radix - accessibility error because the dialog had no screen-reader title. The - palette now includes a visually hidden `DialogTitle`, and the interaction - test asserts that opening and closing it emits no console error. -- **[P2, fixed] Palette focus restoration:** Closing Search initially returned - focus to the document body. The shell now remembers the invoking element and - restores focus after Radix closes. -- **[P2, fixed] Control sizing:** Root font sizing made Tailwind rem-based - 40px controls render at 35px. Search and navigation rows now use explicit - 40px dimensions and full 13–14px labels. -- **[P3] Silent G-chord timeout:** A mistimed chord has no feedback. This is - acceptable for a secondary expert accelerator, but could gain a tiny - transient key hint if real usage shows failures. -- **[P3] Destination descriptions rely on tooltips:** First-time users may - need a little exploration to distinguish Work, Board, Review, and Testing. - Existing product labels were preserved deliberately. - -## Persona Red Flags - -- **Power user:** Search and G chords are fast, but the 500ms G timeout may - feel unforgiving until learned. -- **First-timer:** The grouping helps, though the differences among Work, - Board, Review, and Testing are learned through tooltips and page content. -- **Keyboard or low-vision user:** The final build has a global amber focus - ring, 40px controls, AA contrast, semantic groups, text labels, and focus - restoration. No major barrier remains in the rail. - -## Minor Observations - -- The 224px rail stays proportionate at the configured 900px minimum window. -- The warm ambient wash respects the single-accent rule. -- The resource chip is absent in browser fallback because Tauri resource data - is unavailable; it remains present in the desktop runtime. - -## Questions to Consider - -- Should future usage evidence show the current repository or verification run - in this rail, or should project context stay inside the owning workspaces? -- If users do not discover G chords, would one compact shortcuts hint be more - useful than permanent suffixes? diff --git a/.impeccable/critique/2026-08-01T17-55-01Z__apps-desktop-src-app-tsx.md b/.impeccable/critique/2026-08-01T17-55-01Z__apps-desktop-src-app-tsx.md deleted file mode 100644 index 2a6b1ba4..00000000 --- a/.impeccable/critique/2026-08-01T17-55-01Z__apps-desktop-src-app-tsx.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -target: current CodeVetter desktop product and UI -total_score: 23 -max_score: 40 -na_heuristics: -p0_count: 1 -p1_count: 4 -timestamp: 2026-08-01T17-55-01Z -slug: apps-desktop-src-app-tsx ---- -# CodeVetter product and desktop critique - -## Strategic verdict - -CodeVetter is impressive engineering but not yet a coherent product. The repository contains a substantial local verification stack: bounded execution, runtime receipts, structural and historical evidence, deterministic scoring, a qualified synthetic task corpus, CLI/MCP boundaries, and unusually honest failure states. But that core is buried beneath an older AI-review workbench, repository-intelligence suite, usage dashboard, agent workspace, board, and native agent presentation layer. - -The July pivot exists in product documentation and newer harness work. It does not yet exist as the user's product. The desktop's default object is still a dashboard or repository; it should be a verification case. - -The focused job should be: - -> Given a task and an agent-authored change, did it actually work? Show the executable evidence, state what remains unverified, and make the result reproducible. - -Comparative agent and context experiments are the second job, powered by the same receipts. Graph context is an experimental input, not the product. - -## Competition - -The tools initially identified are several different markets: - -- pgGraph and HydraDB are graph infrastructure. They are not meaningful product competitors. -- CodeGraph, Graphify, and RepoWise are agent-readable context engines. RepoWise also spans human wiki, history, decisions, and code health, creating direct overlap with Repo Unpack. -- DeepWiki is primarily human-readable generated documentation and grounded Q&A. -- Sourcegraph is enterprise code search and multi-repository context. -- CodeRabbit and Qodo compete with the legacy Review proposition and have much stronger pull-request distribution. -- Harbor/Terminal-Bench and SWE-bench occupy coding-agent benchmark infrastructure. -- Braintrust and LangSmith occupy general experiment, dataset, scoring, tracing, and comparison infrastructure. - -CodeVetter should not try to beat focused context providers at indexing, established review vendors at PR distribution, or general evaluation platforms at horizontal breadth. Its credible wedge is local, software-specific, execution-backed verification with hidden checks, immutable evidence identities, contamination detection, and reproducible comparisons. - -## Design Health Score - -| # | Heuristic | Score | Key issue | -|---|---|---:|---| -| 1 | Visibility of System Status | 3 | Strong local states, but no unified verification-run status across surfaces. | -| 2 | Match System / Real World | 2 | Repo Unpack, T-Rex, warm verification, and Review with Claude obscure the core job. | -| 3 | User Control and Freedom | 3 | Good cancellation, retry, persistence, and reversible actions; deeper exits and undo vary. | -| 4 | Consistency and Standards | 2 | Coherent tokens, inconsistent page structures and navigation documentation. | -| 5 | Error Prevention | 3 | Strong validation and confirmations, but advanced forms expose too many paths. | -| 6 | Recognition Rather Than Recall | 2 | Users must remember how Repo, Review, Testing, and Work compose. | -| 7 | Flexibility and Efficiency | 3 | Strong shortcuts, persistent state, history, and expert affordances. | -| 8 | Aesthetic and Minimalist Design | 2 | Visually disciplined but functionally overloaded. | -| 9 | Error Recovery | 2 | Several actionable errors, but no consistent guided recovery model. | -| 10 | Help and Documentation | 1 | Onboarding teaches the outdated review product rather than verification evidence. | -| **Total** | | **23/40** | **Acceptable craft; substantial product simplification required.** | - -## Design Specificity Verdict - -### Design assessment - -Visually authored, structurally unfocused. The dark ink and warm amber Evidence Bench language is coherent and appropriate. The app feels technically serious. But the shell presents several historical products as peers, so it reads as a consolidated suite rather than one verification instrument. - -### Deterministic scan - -The detector reported 10 `gray-on-color` findings: five in Home, three in AgentPanel, and two in QuickReview. Source inspection makes six definite false positives and the remaining four likely false positives because the backgrounds are mutually exclusive branches or very low-opacity tints over dark surfaces. The scan did not reveal a systemic mechanical design defect. - -This reinforces the main conclusion: the highest-impact UI problems are information architecture, terminology, and hierarchy—not Tailwind color cleanup. - -### Visual overlays - -No reliable visual overlay is available. Browser control reported no connected browser, so mutable injection and screenshots could not be performed. Five representative Vite routes returned HTTP 200, which confirms routing only, not rendered quality. - -## Overall Impression - -The strongest moments are the honest receipt and no-confidence states in Testing and Review. The weakest moment is the product entrance: onboarding teaches model selection and AI review, then the app opens on usage telemetry. A user must cross several legacy concepts before reaching the differentiated product. - -The biggest opportunity is not a redesign of each page. It is choosing one canonical object—`verification case`—and reorganizing everything around it. - -## What's Working - -- Honest semantic states such as partial coverage, passed with limits, and no confidence are unusually good. -- Persistent routes, cancellation, retries, bounded output, and history show excellent operational care. -- The ink/amber system, evidence typography, focus treatment, and written status labels are a solid craft foundation worth preserving. - -## Priority Issues - -### P0 — The visible product contradicts the stated product - -**Why it matters:** The repo says CLI/MCP verification is primary and desktop is a receipt viewer. The app leads with Usage, Repo Unpack, Work, Board, Review, and Testing. The landing page still sells a desktop AI reviewer and makes claims about vulnerability classes and offline behavior. Users cannot form a stable expectation. - -**Fix:** Pick the verification product explicitly. Rewrite landing, onboarding, navigation, and the default route around one verification case. Remove unsupported claims and demote unrelated surfaces. - -**Suggested command:** `$impeccable shape` - -### P1 — The shell contradicts the core loop - -**Why it matters:** Launching into usage telemetry makes administration feel more important than determining whether a change is correct. Work and Board are agent-control products placed inside Verification. - -**Fix:** Use a minimal shell such as Verify, Runs, Experiments, and Settings. Put repository context inside a case; move Usage, Work, Board, and Agent Island to Labs/Legacy or remove them from primary navigation. - -**Suggested command:** `$impeccable distill` - -### P1 — Review and Testing split one user question across two products - -**Why it matters:** A user asks whether a change is correct. Review emphasizes model findings; Testing owns the strongest executable receipts. The user must mentally merge them. - -**Fix:** Model a verification case with stages: target and intent, checks, findings, runtime evidence, verdict, limitations, and next action. - -**Suggested command:** `$impeccable shape` - -### P1 — Results bury the verdict beneath accumulated features - -**Why it matters:** Review's sidebar contains roughly a dozen evidence, graph, QA, export, and audience systems. Equal visual weight makes source-backed limitations and next actions hard to locate. - -**Fix:** Pin verdict, evidence strength, limitations, and next action. Move graphs, audience simulation, X-Ray, synthetic QA, and exports behind secondary disclosure. - -**Suggested command:** `$impeccable distill` - -### P1 — Onboarding installs the wrong mental model - -**Why it matters:** It teaches model selection, usage stats, and AI review instead of task completion and executable proof. - -**Fix:** First run should select a repository/change, run one bounded check, and teach how to read a receipt, failure, and limitation. - -**Suggested command:** `$impeccable onboard` - -### P2 — Dense evidence presentation strains accessibility - -**Why it matters:** Critical context is often 9–11px and muted; dense sidebars create long keyboard paths. - -**Fix:** Increase essential evidence metadata size and contrast, simplify result order, and confirm effective runtime contrast visually. - -**Suggested command:** `$impeccable audit` - -## Cognitive Load - -High: seven of eight checklist areas fail. Grouping is generally good, but single focus, chunking, hierarchy, one-thing-at-a-time flow, minimal choices, working-memory burden, and progressive disclosure do not. - -Decision points above four include: - -- six primary destinations plus Settings and command search; -- up to eight Repo Unpack sections; -- eleven Settings categories; -- roughly a dozen Review result-side modules; and -- seven setup concepts inside expanded Review context. - -## Emotional Journey - -The user expects verification, encounters usage administration, becomes uncertain about which surface owns the task, then finally reaches excellent evidence language in Testing. The product peaks late and ends without one calm closure: verified, failed, or no confidence, followed by the next safe action. - -## Persona Red Flags - -**Alex, power user:** Strong shortcuts and persistent state do not answer whether the same change belongs in Repo, Review, or Testing. A trustworthy evaluation in under a minute is unlikely. - -**Jordan, first-timer:** Usage telemetry and AI-review onboarding create the wrong model before they encounter Repo Unpack, T-Rex, warm verification, and scenario compilation. - -**Sam, keyboard/low-vision user:** Focus and reduced-motion support are positive, but tiny muted evidence text and the long Review sidebar journey reduce practical accessibility. - -## Minor Observations - -- Design and surface documentation describe a top rail while implementation uses a fixed left rail. -- Board has a keyboard shortcut but is absent from the command palette. -- Page-title structures differ substantially by route. -- T-Rex is internal-history branding, not self-explanatory product language. -- The sidebar subtitle Evidence workbench is good; the rest of the IA does not yet fulfill it. -- The four largest page files total roughly 14,900 lines, mirroring feature and state accumulation in the user experience. - -## Questions to Consider - -- If Usage, Work, Board, Agent Island, and most Repo Unpack sections disappeared from primary navigation, would the actual verification product lose anything essential? -- Why are Review and Testing separate when the user asks one question: is this change correct? -- Does a panel change the verdict or explain its confidence? If not, why is it in the primary result view? -- Is CodeVetter a daily verification tool, an evaluation research lab, or a broad agent workbench? It cannot lead with all three. diff --git a/.impeccable/critique/2026-08-10T18-31-57Z__apps-desktop-src-pages-home-tsx.md b/.impeccable/critique/2026-08-10T18-31-57Z__apps-desktop-src-pages-home-tsx.md deleted file mode 100644 index 6e31755f..00000000 --- a/.impeccable/critique/2026-08-10T18-31-57Z__apps-desktop-src-pages-home-tsx.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -target: Usage telemetry evidence tiers -total_score: 36 -max_score: 40 -na_heuristics: -p0_count: 0 -p1_count: 0 -timestamp: 2026-08-10T18-31-57Z -slug: apps-desktop-src-pages-home-tsx ---- -## Design Health Score - -| # | Heuristic | Score | Key issue | -|---|---|---:|---| -| 1 | Visibility of system status | 4 | Verified, partial, stale, pending, and loading states are written explicitly. | -| 2 | Match system / real world | 3 | API-equivalent remains specialist language, now explained as not subscription spend. | -| 3 | User control and freedom | 4 | Reconcile and recovery settings are available at the diagnosis. | -| 4 | Consistency and standards | 4 | One reconciliation verb now owns the refresh path. | -| 5 | Error prevention | 4 | Legacy, ambiguous, stale, and unpriced data cannot masquerade as verified. | -| 6 | Recognition rather than recall | 4 | Recovery settings are linked in context. | -| 7 | Flexibility and efficiency | 3 | Aggregate categories are not yet drillable to individual sources. | -| 8 | Aesthetic and minimalist design | 4 | Evidence hierarchy is compact and uses the incumbent workbench language. | -| 9 | Error recovery | 3 | Recovery is complete, but source-level diagnostics remain aggregate. | -| 10 | Help and documentation | 3 | Inline pricing and recovery explanations cover the main uncertainty model. | -| **Total** | | **36/40** | **Excellent** | - -## Design Specificity Verdict - -The result is authored for CodeVetter's Evidence Bench. Accepted transcript observations, -scanner revision, observation watermark, exact/ranged/unpriced pricing, and explicit legacy -exclusion make the surface an evidence instrument rather than a generic analytics card. - -The deterministic detector returned five `gray-on-color` warnings in Home.tsx and none in -Settings.tsx. All five are contextual false positives: the background is translucent over ink or -the slate text classes are mutually exclusive with the cyan active state. Verified detector issue -count: zero. - -## Overall Impression - -The trusted number leads, uncertainty is written rather than hidden, and recovery is attached to -the diagnosis. The remaining opportunity is source/session drill-down, not another visual layer. - -## What's Working - -- Verified totals and legacy estimates are structurally separated. -- Cost bounds explain unknown service tier and disclaim subscription spend. -- Recovery is one bounded flow: import roots, then re-index and reconcile. - -## Priority Issues - -- **P2 — Aggregate diagnostics are not drillable.** Users can see affected counts but not the - source identities. Add a source-detail disclosure after the read cutover is qualified. -- **P3 — Narrow screenshots compress below the product contract.** The Tauri app enforces a 900px - minimum; 390px is retained as evidence but is not a supported window state. - -## Persona Red Flags - -- **Alex:** source-level evidence is not yet inspectable from the aggregate. -- **Sam:** the cost range is now explicitly API-equivalent and not subscription spend; written - partial coverage does not rely on color. -- **Riley:** import persistence failures are announced and the recovery action returns to a single - reconciliation path. - -## Minor Observations - -- Legacy period estimates remain expanded for continuity; a later release may collapse them once - users have migrated to verified reads. -- The app's documented and configured minimum width is 900px, so mobile-shell adaptation is out of - scope for this macOS desktop viewer. - -## Questions to Consider - -- Should the next qualified iteration expose the exact sessions behind each unresolved tier? -- Once verified coverage stabilizes, should the legacy blended summary become collapsed by default? diff --git a/.impeccable/critique/2026-08-15T20-33-20Z__apps-desktop-src-components-app-error-boundary-tsx.md b/.impeccable/critique/2026-08-15T20-33-20Z__apps-desktop-src-components-app-error-boundary-tsx.md deleted file mode 100644 index 3c800eac..00000000 --- a/.impeccable/critique/2026-08-15T20-33-20Z__apps-desktop-src-components-app-error-boundary-tsx.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -target: apps/desktop/src/components/app-error-boundary.tsx -total_score: 35 -maximum: 40 -p0: 0 -p1: 0 -p2: 1 -method: dual-agent -timestamp: 2026-08-15T20-33-20Z -slug: apps-desktop-src-components-app-error-boundary-tsx ---- -# CodeVetter crash recovery critique - -## Method - -Dual-agent review: a detector-blind visual/heuristic assessment plus an independent detector and responsive-browser evidence pass. The final state was then rechecked at 390, 768, and 1440 px after resolving the review findings. - -## Nielsen assessment — 35/40 - -| Heuristic | Score | Final assessment | -| --- | ---: | --- | -| Visibility of system status | 3 | The interruption, local receipt, and copy status are explicit; repeated retry has no attempt counter. | -| Match to the real world | 4 | Scope-aware language and plain recovery actions describe what happened and what each action does. | -| User control and freedom | 3 | Retry, reload, and Usage escape cover the common exits; Usage remains a best-effort app route. | -| Consistency and standards | 4 | Uses the established ink surface, amber action, semantic rose state, type, buttons, and focus treatment. | -| Error prevention | 3 | The boundary contains the failure and avoids unsupported safety claims; it does not add a repeated-failure safe mode. | -| Recognition over recall | 4 | Actions are visible and retry/reload behavior is stated directly. | -| Flexibility and efficiency | 3 | Keyboard recovery and copyable diagnostics are available without exposing raw details by default. | -| Aesthetic and minimalist design | 4 | The hierarchy stays focused: interruption, recovery, then local evidence. | -| Error recognition and recovery | 4 | Scope, three recovery routes, incident identity, and technical evidence are all visible. | -| Help and documentation | 3 | Technical details support reporting, but no dedicated troubleshooting route is present. | - -## Cognitive load — 8/8 - -The surface has one focus, three clearly grouped recovery choices, a short behavioral explanation, and progressive disclosure for diagnostics. No decision point exceeds four choices. - -## Accessibility and responsive evidence - -- Focus moves to the recovery heading on mount; the next Tab reaches the primary recovery action. -- The full-page alert was narrowed to the interruption announcement, leaving controls outside the live alert. -- Muted metadata uses the higher-contrast zinc-400 token. -- Axe reported no critical or serious violations in the focused Playwright check. -- Document scroll width matched client width at 390, 768, and 1440 px. - -## Findings resolved - -- **P1 resolved:** removed the categorical claim that the repository was unmodified. The UI now states that repository state was not checked. -- **P1 resolved:** application-shell failures now always expose a Return to Usage action in addition to retry and reload. -- **P2 resolved:** recovery takes focus, metadata contrast was raised, and retry versus reload behavior is explained. - -## Remaining advisory item - -- **P2:** if the same render failure repeats, the surface does not yet count attempts or escalate to a dedicated safe mode. This is a future reliability enhancement, not a blocker for the bounded recovery layer. - -## Detector and integrity - -The advisory detector returned an empty result (`[]`) across the recovery component and entry point. No production dependency was added, raw error messages and stacks are not persisted, and repository/query data is excluded from the local incident receipt. diff --git a/.impeccable/critique/2026-08-15T21-39-48Z__apps-desktop-src-pages-performance-tsx.md b/.impeccable/critique/2026-08-15T21-39-48Z__apps-desktop-src-pages-performance-tsx.md deleted file mode 100644 index 3e1f0590..00000000 --- a/.impeccable/critique/2026-08-15T21-39-48Z__apps-desktop-src-pages-performance-tsx.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -timestamp: 2026-08-15T21-39-48Z -slug: apps-desktop-src-pages-performance-tsx ---- -# Performance workbench critique - -Target: `apps/desktop/src/pages/Performance.tsx` - -## Outcome - -- Design heuristic score: 35/40 (good, near excellent). -- Automated detector: 0 findings. -- Responsive qualification: no horizontal overflow at 390, 768, or 1440 px. -- Accessibility structure: one main landmark, labelled workload controls, labelled evidence region, and accessible form names. -- Final severity: 0 P0, 0 P1. - -## Resolved during critique - -- Added a real same-scope paired-verification action and verdict-driven campaign states. -- Invalidated stale evidence when scope fields or the selected repository change. -- Cancelled and discarded late receipts from a prior repository generation. -- Added truthful blocked, failed, and no-confidence recovery states. -- Separated observed, inferred, and unverified evidence without truncating captured rows. -- Raised low-contrast operational copy and removed empty machine-detail rows. - -## Evidence - -- `artifacts/design/product-surfaces-after-390.jpg` -- `artifacts/design/product-surfaces-after-768.jpg` -- `artifacts/design/product-surfaces-after-1440.jpg` diff --git a/.impeccable/design.json b/.impeccable/design.json deleted file mode 100644 index ad45e6cb..00000000 --- a/.impeccable/design.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "schemaVersion": 2, - "generatedAt": "2026-07-29T00:00:00.000Z", - "title": "Design System: CodeVetter", - "extensions": { - "colorMeta": { - "canvas-ink": { - "role": "neutral", - "displayName": "Canvas Ink", - "canonical": "#060708", - "tonalRamp": [ - "#060708", - "#0c0d0f", - "#111316", - "#17191d", - "#35383e", - "#6c7078", - "#a1a1aa", - "#f4f4f5" - ] - }, - "action-amber": { - "role": "primary", - "displayName": "Action Amber", - "canonical": "#f3ad3d", - "tonalRamp": [ - "#2a1b05", - "#4b3008", - "#71490d", - "#9b6818", - "#c88728", - "#f3ad3d", - "#ffc75e", - "#fff0c7" - ] - }, - "failure-rose": { - "role": "semantic", - "displayName": "Failure Rose", - "canonical": "#fb7185", - "tonalRamp": [ - "#2e080e", - "#54121d", - "#7f2030", - "#aa3448", - "#d94f65", - "#fb7185", - "#fda4af", - "#ffe4e6" - ] - }, - "verified-green": { - "role": "semantic", - "displayName": "Verified Green", - "canonical": "#4ade80", - "tonalRamp": [ - "#052e16", - "#14532d", - "#166534", - "#15803d", - "#22c55e", - "#4ade80", - "#86efac", - "#dcfce7" - ] - } - }, - "typographyMeta": { - "title": { - "displayName": "Workbench Title", - "purpose": "Page, panel, and evidence-section headings." - }, - "body": { - "displayName": "Operating Body", - "purpose": "Instructions, summaries, and supporting context." - }, - "label": { - "displayName": "Compact Label", - "purpose": "Fields, controls, metrics, and metadata." - }, - "evidence": { - "displayName": "Evidence Mono", - "purpose": "Paths, revisions, commands, and machine identities." - } - }, - "shadows": [ - { - "name": "surface-ambient", - "value": "0 28px 80px -52px rgba(0, 0, 0, 0.92)", - "purpose": "Diffuse depth for major cards and overlays." - }, - { - "name": "action-warm", - "value": "0 12px 30px -18px rgba(243, 173, 61, 0.9)", - "purpose": "Restrained emphasis for primary action controls." - } - ], - "motion": [ - { - "name": "control-state", - "value": "150ms ease", - "purpose": "Color, border, shadow, and pressed-state transitions." - }, - { - "name": "content-enter", - "value": "200ms ease-out", - "purpose": "Short opacity and 4px translate entrance for newly available content." - } - ], - "breakpoints": [ - { - "name": "sm", - "value": "640px" - }, - { - "name": "lg", - "value": "1024px" - }, - { - "name": "desktop-window-min", - "value": "900px" - } - ] - }, - "components": [ - { - "name": "Primary Button", - "kind": "button", - "refersTo": "button-primary", - "description": "The single intentional action within a verification context.", - "html": "", - "css": ".ds-button-primary { height: 40px; padding: 8px 16px; border: 1px solid rgba(253,230,138,.2); border-radius: 10px; background: var(--cv-accent, #f3ad3d); color: #211609; font: 500 14px/1.25 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",sans-serif; box-shadow: 0 12px 30px -18px rgba(243,173,61,.9), inset 0 1px 0 rgba(255,255,255,.3); transition: background-color 150ms ease, transform 150ms ease; } .ds-button-primary:hover { background: var(--cv-accent-strong, #ffc75e); } .ds-button-primary:focus-visible { outline: 2px solid rgba(243,173,61,.88); outline-offset: 2px; } .ds-button-primary:active { transform: translateY(1px); }" - }, - { - "name": "Outline Button", - "kind": "button", - "refersTo": "button-outline", - "description": "A bounded secondary action that does not compete with execution.", - "html": "", - "css": ".ds-button-outline { height: 40px; padding: 8px 16px; border: 1px solid rgba(255,255,255,.11); border-radius: 10px; background: rgba(255,255,255,.035); color: #e4e4e7; font: 500 14px/1.25 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",sans-serif; box-shadow: inset 0 1px 0 rgba(255,255,255,.04); transition: background-color 150ms ease, border-color 150ms ease; } .ds-button-outline:hover { border-color: rgba(255,255,255,.18); background: rgba(255,255,255,.075); color: #fff; } .ds-button-outline:focus-visible { outline: 2px solid rgba(243,173,61,.88); outline-offset: 2px; }" - }, - { - "name": "Evidence Input", - "kind": "input", - "refersTo": "input", - "description": "A compact field for URLs, ranges, and verification parameters.", - "html": "", - "css": ".ds-input { width: 100%; height: 40px; padding: 8px 12px; border: 1px solid rgba(255,255,255,.1); border-radius: 10px; background: rgba(255,255,255,.035); color: #f4f4f5; font: 400 14px/1.5 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",sans-serif; box-shadow: inset 0 1px 0 rgba(255,255,255,.025); transition: background-color 150ms ease, border-color 150ms ease, box-shadow 150ms ease; } .ds-input:hover { border-color: rgba(255,255,255,.15); } .ds-input:focus-visible { outline: 2px solid rgba(243,173,61,.15); outline-offset: 2px; border-color: rgba(252,211,77,.35); background: rgba(255,255,255,.05); }" - }, - { - "name": "Verification Card", - "kind": "card", - "refersTo": "card", - "description": "The primary workbench plane for one verification mechanism.", - "html": "

Test change in preview

Resolve exact source identity and return browser evidence.

", - "css": ".ds-card { padding: 20px; border: 1px solid rgba(255,255,255,.075); border-radius: 12px; background: var(--cv-surface, #0c0d0f); color: #f4f4f5; box-shadow: 0 24px 70px -50px rgba(0,0,0,.95), inset 0 1px 0 rgba(255,255,255,.025); } .ds-card h3 { margin: 0; font: 600 18px/1.25 \"SF Pro Display\",-apple-system,sans-serif; letter-spacing: -.018em; } .ds-card p { margin: 6px 0 0; color: #a1a1aa; font: 400 14px/1.5 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",sans-serif; }" - }, - { - "name": "Evidence Badge", - "kind": "chip", - "refersTo": "badge", - "description": "A written status or scope qualifier paired with semantic color.", - "html": "Passed with limits", - "css": ".ds-badge { display: inline-flex; min-height: 24px; align-items: center; padding: 4px 10px; border: 1px solid rgba(252,211,77,.2); border-radius: 9999px; background: rgba(252,211,77,.1); color: #fde68a; font: 500 12px/1 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",sans-serif; transition: background-color 150ms ease; } .ds-badge:hover { background: rgba(252,211,77,.16); } .ds-badge:focus-visible { outline: 2px solid rgba(243,173,61,.88); outline-offset: 2px; }" - } - ], - "narrative": { - "northStar": "The Evidence Bench", - "overview": "CodeVetter feels like a precise local instrument: dark, quiet, dense enough for technical work, and candid about the strength of every claim. Warm amber marks the next intentional action. Semantic colors communicate verified, warning, or failure states only when the same meaning is also written in text or expressed with an icon. The interface should recede behind source identities, runtime results, and limitations.", - "keyCharacteristics": [ - "Ink surfaces separated by restrained tonal steps and hairline borders.", - "Compact native-feeling controls with generous focus treatment.", - "Warm amber used sparingly for action, selection, and verification emphasis.", - "Monospace reserved for paths, revisions, commands, and evidence identities.", - "Every state remains understandable without color alone." - ], - "rules": [ - { - "name": "The One Warm Voice Rule", - "body": "Amber identifies intentional action or active verification context; it is not ambient decoration.", - "section": "colors" - }, - { - "name": "The Written State Rule", - "body": "Green, gold, rose, and blue may reinforce meaning, but a label or icon must communicate the same state.", - "section": "colors" - }, - { - "name": "The Evidence Type Rule", - "body": "Monospace signals data a user may compare, copy, or feed to another tool; prose and actions stay in the system sans.", - "section": "typography" - }, - { - "name": "The Flat Evidence Rule", - "body": "Evidence rows are stable nested planes; hover lift and decorative transform are reserved for actionable controls.", - "section": "elevation" - } - ], - "dos": [ - "Do lead with the action, exact identity, verdict, and limitation.", - "Do reuse the established card, input, button, badge, and focus patterns.", - "Do keep verification forms compact and preserve evidence below the action.", - "Do provide loading, empty, error, limited, failed, and no-confidence states with plain-language labels." - ], - "donts": [ - "Don't present model opinion, topology, or a fixture as executable proof.", - "Don't use amber across large decorative regions or for non-action accents.", - "Don't communicate pass, warning, or failure through color alone.", - "Don't add floating glass cards, hero typography, or agent theater to operating surfaces." - ] - } -} diff --git a/research/codebase-context-tools-landscape.md b/docs/knowledge/codebase-context-tools-landscape.md similarity index 99% rename from research/codebase-context-tools-landscape.md rename to docs/knowledge/codebase-context-tools-landscape.md index 167f1dc1..488b3574 100644 --- a/research/codebase-context-tools-landscape.md +++ b/docs/knowledge/codebase-context-tools-landscape.md @@ -1,3 +1,10 @@ +--- +title: Codebase context tools landscape +description: Survey of codebase indexing, context-provider, and documentation tools evaluated for CodeVetter (research snapshot, April 2026). +sidebar: + order: 90 +--- + # Codebase Context Tools for AI Agents & AI Code Review ## Complete Landscape Research - April 2026 From 3de2b4941d1498a66fa21869e4fc2af0b0586008 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 13:40:26 +0530 Subject: [PATCH 03/37] refactor(benchmarks): fold the public corpus into one benchmarks/ root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit benchmark/ and benchmarks/ differed by a single character while holding different things: the public hand-labeled catch-rate corpus, and the internal evaluation corpora (agent-prs, agent-tasks, runtime-challenges, context-*, performance-lab). Move the former to benchmarks/public-catch-rate/ so there is one corpus root and no singular/plural ambiguity. The published surface is unchanged. scripts/generate-benchmark-dataset.mjs still emits apps/landing-page-astro/public/benchmark/codevetter-benchmark-v1.json and the site still serves /benchmark/... — only the repo-root source directory moved. Verified: pnpm bench:public scores all 27 cases from the new path, the generator reads it, and the Astro build produces all 24 pages. Deliberately not rewritten: the pinned-SHA GitHub raw URLs in Bento.astro (they resolve against commit 006c2e5c, where the old path still exists), the public /benchmark/ URLs in agent-markdown.ts and benchmark.astro, and docs/development/performance.md's --target benchmark/parser.mjs, which is an unrelated perf example rather than the corpus. Co-Authored-By: Claude Opus 5 (1M context) --- PROJECT_STATUS.md | 4 ++-- apps/desktop/src-tauri/src/commands/review.rs | 4 ++-- apps/landing-page-astro/src/pages/benchmark.astro | 4 ++-- {benchmark => benchmarks/public-catch-rate}/README.md | 8 ++++---- .../public-catch-rate}/cases/go-errcheck/label.json | 0 .../public-catch-rate}/cases/go-errcheck/source.go | 0 .../cases/go-hardcoded-credentials/label.json | 0 .../cases/go-hardcoded-credentials/source.go | 0 .../public-catch-rate}/cases/go-nil-pointer/label.json | 0 .../public-catch-rate}/cases/go-nil-pointer/source.go | 0 .../cases/go-race-condition/label.json | 0 .../cases/go-race-condition/source.go | 0 .../cases/go-sql-injection/label.json | 0 .../cases/go-sql-injection/source.go | 0 .../cases/java-insecure-random/label.json | 0 .../cases/java-insecure-random/source.java | 0 .../cases/js-eval-injection/label.json | 0 .../cases/js-eval-injection/source.js | 0 .../cases/js-open-redirect/label.json | 0 .../cases/js-open-redirect/source.js | 0 .../public-catch-rate}/cases/py-bare-except/label.json | 0 .../public-catch-rate}/cases/py-bare-except/source.py | 0 .../cases/py-command-injection/label.json | 0 .../cases/py-command-injection/source.py | 0 .../cases/py-hardcoded-secret/label.json | 0 .../cases/py-hardcoded-secret/source.py | 0 .../cases/py-insecure-deserialization/label.json | 0 .../cases/py-insecure-deserialization/source.py | 0 .../cases/py-path-traversal/label.json | 0 .../cases/py-path-traversal/source.py | 0 .../cases/py-sql-injection/label.json | 0 .../cases/py-sql-injection/source.py | 0 .../public-catch-rate}/cases/py-ssrf/label.json | 0 .../public-catch-rate}/cases/py-ssrf/source.py | 0 .../public-catch-rate}/cases/py-weak-hash/label.json | 0 .../public-catch-rate}/cases/py-weak-hash/source.py | 0 .../public-catch-rate}/cases/py-zip-bomb/label.json | 0 .../public-catch-rate}/cases/py-zip-bomb/source.py | 0 .../cases/rust-integer-overflow/label.json | 0 .../cases/rust-integer-overflow/source.rs | 0 .../public-catch-rate}/cases/ts-dead-code/label.json | 0 .../public-catch-rate}/cases/ts-dead-code/source.ts | 0 .../cases/ts-hardcoded-credentials/label.json | 0 .../cases/ts-hardcoded-credentials/source.ts | 0 .../cases/ts-insecure-cookie/label.json | 0 .../cases/ts-insecure-cookie/source.ts | 0 .../cases/ts-missing-await/label.json | 0 .../cases/ts-missing-await/source.ts | 0 .../cases/ts-prototype-pollution/label.json | 0 .../cases/ts-prototype-pollution/source.ts | 0 .../public-catch-rate}/cases/ts-regex-dos/label.json | 0 .../public-catch-rate}/cases/ts-regex-dos/source.ts | 0 .../cases/ts-sql-injection/label.json | 0 .../cases/ts-sql-injection/source.ts | 0 .../cases/ts-type-confusion/label.json | 0 .../cases/ts-type-confusion/source.ts | 0 .../public-catch-rate}/cases/ts-xss/label.json | 0 .../public-catch-rate}/cases/ts-xss/source.tsx | 0 .../reviews-raw/go-errcheck.codevetter.raw.json | 0 .../go-hardcoded-credentials.codevetter.raw.json | 0 .../reviews-raw/go-nil-pointer.codevetter.raw.json | 0 .../reviews-raw/go-race-condition.codevetter.raw.json | 0 .../reviews-raw/go-sql-injection.codevetter.raw.json | 0 .../java-insecure-random.codevetter.raw.json | 0 .../reviews-raw/js-eval-injection.codevetter.raw.json | 0 .../reviews-raw/js-open-redirect.codevetter.raw.json | 0 .../reviews-raw/py-bare-except.codevetter.raw.json | 0 .../py-command-injection.codevetter.raw.json | 0 .../py-hardcoded-secret.codevetter.raw.json | 0 .../py-insecure-deserialization.codevetter.raw.json | 0 .../reviews-raw/py-path-traversal.codevetter.raw.json | 0 .../reviews-raw/py-sql-injection.codevetter.raw.json | 0 .../reviews-raw/py-ssrf.codevetter.raw.json | 0 .../reviews-raw/py-weak-hash.codevetter.raw.json | 0 .../reviews-raw/py-zip-bomb.codevetter.raw.json | 0 .../rust-integer-overflow.codevetter.raw.json | 0 .../reviews-raw/ts-dead-code.codevetter.raw.json | 0 .../ts-hardcoded-credentials.codevetter.raw.json | 0 .../reviews-raw/ts-insecure-cookie.codevetter.raw.json | 0 .../reviews-raw/ts-missing-await.codevetter.raw.json | 0 .../ts-prototype-pollution.codevetter.raw.json | 0 .../reviews-raw/ts-regex-dos.codevetter.raw.json | 0 .../reviews-raw/ts-sql-injection.codevetter.raw.json | 0 .../reviews-raw/ts-type-confusion.codevetter.raw.json | 0 .../reviews-raw/ts-xss.codevetter.raw.json | 0 biome.json | 2 +- docs/development/benchmark.md | 2 +- docs/operations/automation-contract.md | 2 +- docs/product/overview.md | 2 +- scripts/generate-benchmark-dataset.mjs | 2 +- scripts/map-benchmark-reviews.mjs | 4 ++-- scripts/run-public-benchmark.mjs | 10 +++++----- 92 files changed, 22 insertions(+), 22 deletions(-) rename {benchmark => benchmarks/public-catch-rate}/README.md (95%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-errcheck/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-errcheck/source.go (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-hardcoded-credentials/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-hardcoded-credentials/source.go (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-nil-pointer/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-nil-pointer/source.go (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-race-condition/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-race-condition/source.go (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-sql-injection/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/go-sql-injection/source.go (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/java-insecure-random/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/java-insecure-random/source.java (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/js-eval-injection/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/js-eval-injection/source.js (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/js-open-redirect/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/js-open-redirect/source.js (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-bare-except/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-bare-except/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-command-injection/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-command-injection/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-hardcoded-secret/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-hardcoded-secret/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-insecure-deserialization/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-insecure-deserialization/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-path-traversal/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-path-traversal/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-sql-injection/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-sql-injection/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-ssrf/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-ssrf/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-weak-hash/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-weak-hash/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-zip-bomb/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/py-zip-bomb/source.py (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/rust-integer-overflow/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/rust-integer-overflow/source.rs (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-dead-code/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-dead-code/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-hardcoded-credentials/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-hardcoded-credentials/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-insecure-cookie/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-insecure-cookie/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-missing-await/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-missing-await/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-prototype-pollution/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-prototype-pollution/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-regex-dos/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-regex-dos/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-sql-injection/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-sql-injection/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-type-confusion/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-type-confusion/source.ts (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-xss/label.json (100%) rename {benchmark => benchmarks/public-catch-rate}/cases/ts-xss/source.tsx (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/go-errcheck.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/go-hardcoded-credentials.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/go-nil-pointer.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/go-race-condition.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/go-sql-injection.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/java-insecure-random.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/js-eval-injection.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/js-open-redirect.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-bare-except.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-command-injection.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-hardcoded-secret.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-insecure-deserialization.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-path-traversal.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-sql-injection.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-ssrf.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-weak-hash.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/py-zip-bomb.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/rust-integer-overflow.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-dead-code.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-hardcoded-credentials.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-insecure-cookie.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-missing-await.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-prototype-pollution.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-regex-dos.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-sql-injection.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-type-confusion.codevetter.raw.json (100%) rename {benchmark => benchmarks/public-catch-rate}/reviews-raw/ts-xss.codevetter.raw.json (100%) diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index 2275663f..a38d40a9 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -397,7 +397,7 @@ Internal (fleet): - **2026-07-03 — Surface consolidation + finishes (multi-agent pass):** removed redundant standalone pages QaReplay (`/qa-replay`) and IntentDebugger (`/intent-debugger`) — their functionality lives in Review. Finished Rubrics (review↔pack linkage via `local_reviews.standards_pack`, exact prompt preview, per-pack usage stats, pack cloning), T-Rex (per-watcher error recovery + retry, run drill-down dialog with persisted findings/log excerpt, pre-flight gh/token validation, per-PR base-branch inference), and AgentMemories (copy-as-markdown export, substring//regex/ line filter, git-diff-vs-HEAD view with secret redaction). Refactored QuickReview.tsx 6,264→3,050 lines into 12 components + 4 lib modules (behavior-preserving, 15 commits). Raw-Claude baseline scored on the 27 public benchmark cases (catch 0.931 / precision 0.397 / F1 0.557); CodeVetter's own comparator slot still needs generation before head-to-head claims. - **2026-07-03 (shipped in v1.2.8) — By-model cost attribution fix:** session-level `model_used` is last-model-wins, so multi-model Claude sessions booked ALL tokens/cost to the final model (a 211MB session with 17k opus-4-7 messages + 1.6k fable-5 messages billed $3.6k entirely to fable). Fix: per-message `session_model_usage` table populated by the indexer + one-time streaming backfill over existing Claude JSONL; by-model panel and per-session costs now sum per-model parts. Also added Fable/Mythos 5 pricing ($10/$50; was falling to sonnet default), folded `` into "unknown", and removed the Top-projects cost panel from Home (with its query/command/IPC). Verified by replaying the fix over the live DB: opus-4-7 $21,986→$29,473 (was under-credited), fable-5 correctly repriced. Guarded by `multi_model_claude_session_splits_usage_per_model`. - **2026-07-03:** Removed legacy Next.js landing page (`apps/landing-page`) — fully superseded by Astro site; `next-env.d.ts` git-removed, stale doc references cleaned up. -- **2026-07-03:** Published 27 hand-labeled public benchmark cases (`benchmark/cases/`) covering 7 languages (TypeScript, Python, Go, Rust, JavaScript, Java) and 15+ vulnerability types (SQL injection, XSS, hardcoded secrets, race conditions, path traversal, SSRF, prototype pollution, regex DoS, zip bombs, etc.). Scorer script (`scripts/run-public-benchmark.mjs`) validates labels and computes catch-rate/precision/F1 per reviewer. `pnpm bench:public`. Enterprise claims now backed by external, repeatable proof. +- **2026-07-03:** Published 27 hand-labeled public benchmark cases (`benchmarks/public-catch-rate/cases/`) covering 7 languages (TypeScript, Python, Go, Rust, JavaScript, Java) and 15+ vulnerability types (SQL injection, XSS, hardcoded secrets, race conditions, path traversal, SSRF, prototype pollution, regex DoS, zip bombs, etc.). Scorer script (`scripts/run-public-benchmark.mjs`) validates labels and computes catch-rate/precision/F1 per reviewer. `pnpm bench:public`. Enterprise claims now backed by external, repeatable proof. - **2026-07-02/03:** Streamlined telemetry + fleet navigation, guarded manual deploy command in CI, polished repo intelligence evidence surfaces. - **2026-06-28:** Devin agent indexing, agent hide/show filter, Grok parser improvements; PROJECT_STATUS audited as source of truth. - **2026-06-21 (v1.1.99) — Codex cost over-count fix:** Codex reports session-CUMULATIVE token totals; the incremental indexer was ADDING that running total every pass, inflating one session to 61.5B tokens / $35k (true: 391M / ~$220) and making "today" read ~$12.9k. Fix: `tokens_absolute` flag so cumulative tokens are SET not added, plus a one-time `fix_codex_token_totals` repair re-reading each Codex file. Verified on a live-DB copy: today $12,896→$377, year $82k→$38k (Claude cache-read costs, which are real, dominate the remainder). Guarded by `eval_append_delta_sets_cumulative_tokens_but_adds_per_message`. @@ -471,7 +471,7 @@ Internal (fleet): The corpus passes its contract-readiness publication gates; it does not by itself establish agent quality, provider superiority, or product value. - `--evidence-comparison=with:without` mode compares stored outputs with and without deterministic evidence search. -- 27 hand-labeled public benchmark cases (`benchmark/cases/`) covering 7 languages and 15+ vulnerability types; `pnpm bench:public` scores catch-rate/precision/F1. +- 27 hand-labeled public benchmark cases (`benchmarks/public-catch-rate/cases/`) covering 7 languages and 15+ vulnerability types; `pnpm bench:public` scores catch-rate/precision/F1. ### Evidence Pattern Search - Deterministic risk candidate packets from changed files, sensitive paths, optional `ast-grep` structural matches, blast/history context, and verification signals; top candidates and procedure gates injected into review prompts. diff --git a/apps/desktop/src-tauri/src/commands/review.rs b/apps/desktop/src-tauri/src/commands/review.rs index e4feff91..9ad461a6 100644 --- a/apps/desktop/src-tauri/src/commands/review.rs +++ b/apps/desktop/src-tauri/src/commands/review.rs @@ -3495,9 +3495,9 @@ mod tests { use super::*; /// Generate CodeVetter's public-benchmark comparator outputs by running - /// every `benchmark/cases//` through the REAL production review + /// every `benchmarks/public-catch-rate/cases//` through the REAL production review /// pipeline (risk tiers, specialists, coordinator, dedup) headlessly. - /// Raw pipeline output lands in `benchmark/reviews-raw/.codevetter.raw.json`; + /// Raw pipeline output lands in `benchmarks/public-catch-rate/reviews-raw/.codevetter.raw.json`; /// ground-truth mapping is a separate, human-checked step. Requires the /// `claude` CLI on PATH and burns real quota — hence ignored. #[test] diff --git a/apps/landing-page-astro/src/pages/benchmark.astro b/apps/landing-page-astro/src/pages/benchmark.astro index f5b3b17d..5e50cbc6 100644 --- a/apps/landing-page-astro/src/pages/benchmark.astro +++ b/apps/landing-page-astro/src/pages/benchmark.astro @@ -5,7 +5,7 @@ import Footer from '@/components/Footer.astro'; import dataset from '@/../public/benchmark/codevetter-benchmark-v1.json'; import results from '@/data/benchmark-results.json'; -// Real numbers, sourced from benchmark/cases + benchmark/reviews scored by +// Real numbers, sourced from benchmarks/public-catch-rate/cases + .../reviews scored by // scripts/run-public-benchmark.mjs. Regenerate with `node /tmp/gen-benchmark.mjs` // (or the committed generator) after any case/review change. const cv = results.codevetter; @@ -185,7 +185,7 @@ const datasetJsonLd = {

Methodology

    -
  1. Each case lives in benchmark/cases/<id>/ with a source.<ext> and a hand-written label.json ground truth (type, severity, line range, description).
  2. +
  3. Each case lives in benchmarks/public-catch-rate/cases/<id>/ with a source.<ext> and a hand-written label.json ground truth (type, severity, line range, description).
  4. A reviewer's output is normalized into reviews/<case-id>.<reviewer>.json with a matched_ground_truth array per finding.
  5. The scorer (scripts/run-public-benchmark.mjs) computes catch rate, precision, F1, false positives, and redundant matches.
  6. Catch rate = matched ground-truth issues ÷ total expected. Precision = matched ÷ (matched + false positives + redundant). F1 = harmonic mean.
  7. diff --git a/benchmark/README.md b/benchmarks/public-catch-rate/README.md similarity index 95% rename from benchmark/README.md rename to benchmarks/public-catch-rate/README.md index 4cdcf359..555f838b 100644 --- a/benchmark/README.md +++ b/benchmarks/public-catch-rate/README.md @@ -13,7 +13,7 @@ audited. ## Layout ``` -benchmark/ +benchmarks/public-catch-rate/ cases/ / source. # the code snippet with known issues @@ -112,7 +112,7 @@ From the repo root: # This requires no reviewer output and always works. npm run bench:public -# Score a reviewer's output after dropping files into benchmark/reviews/. +# Score a reviewer's output after dropping files into benchmarks/public-catch-rate/reviews/. npm run bench:public -- --reviewer=codevetter # Emit a JSON scorecard. @@ -127,7 +127,7 @@ npm run bench:public -- --reviewer=codevetter --min-rate=0.8 ## How to evaluate a tool against this benchmark -1. For each case in `benchmark/cases//`, feed `source.` to your +1. For each case in `benchmarks/public-catch-rate/cases//`, feed `source.` to your reviewer (CodeVetter or any comparator). 2. Normalize the reviewer's findings into the `reviews/.json` shape above, filling `matched_ground_truth` with the ground-truth ids each finding @@ -149,6 +149,6 @@ npm run bench:public -- --reviewer=codevetter --min-rate=0.8 - Cases are synthetic and self-contained; they are not tied to a specific PR or repo. They exist to make the benchmark reproducible by anyone, anywhere. - The sibling `benchmarks/agent-prs/` harness measures catch rate on real public - agent-generated PRs with preserved review artifacts. This `benchmark/` set + agent-generated PRs with preserved review artifacts. This `benchmarks/public-catch-rate/` set complements it with broad, language- and issue-type coverage that is cheap to re-run. diff --git a/benchmark/cases/go-errcheck/label.json b/benchmarks/public-catch-rate/cases/go-errcheck/label.json similarity index 100% rename from benchmark/cases/go-errcheck/label.json rename to benchmarks/public-catch-rate/cases/go-errcheck/label.json diff --git a/benchmark/cases/go-errcheck/source.go b/benchmarks/public-catch-rate/cases/go-errcheck/source.go similarity index 100% rename from benchmark/cases/go-errcheck/source.go rename to benchmarks/public-catch-rate/cases/go-errcheck/source.go diff --git a/benchmark/cases/go-hardcoded-credentials/label.json b/benchmarks/public-catch-rate/cases/go-hardcoded-credentials/label.json similarity index 100% rename from benchmark/cases/go-hardcoded-credentials/label.json rename to benchmarks/public-catch-rate/cases/go-hardcoded-credentials/label.json diff --git a/benchmark/cases/go-hardcoded-credentials/source.go b/benchmarks/public-catch-rate/cases/go-hardcoded-credentials/source.go similarity index 100% rename from benchmark/cases/go-hardcoded-credentials/source.go rename to benchmarks/public-catch-rate/cases/go-hardcoded-credentials/source.go diff --git a/benchmark/cases/go-nil-pointer/label.json b/benchmarks/public-catch-rate/cases/go-nil-pointer/label.json similarity index 100% rename from benchmark/cases/go-nil-pointer/label.json rename to benchmarks/public-catch-rate/cases/go-nil-pointer/label.json diff --git a/benchmark/cases/go-nil-pointer/source.go b/benchmarks/public-catch-rate/cases/go-nil-pointer/source.go similarity index 100% rename from benchmark/cases/go-nil-pointer/source.go rename to benchmarks/public-catch-rate/cases/go-nil-pointer/source.go diff --git a/benchmark/cases/go-race-condition/label.json b/benchmarks/public-catch-rate/cases/go-race-condition/label.json similarity index 100% rename from benchmark/cases/go-race-condition/label.json rename to benchmarks/public-catch-rate/cases/go-race-condition/label.json diff --git a/benchmark/cases/go-race-condition/source.go b/benchmarks/public-catch-rate/cases/go-race-condition/source.go similarity index 100% rename from benchmark/cases/go-race-condition/source.go rename to benchmarks/public-catch-rate/cases/go-race-condition/source.go diff --git a/benchmark/cases/go-sql-injection/label.json b/benchmarks/public-catch-rate/cases/go-sql-injection/label.json similarity index 100% rename from benchmark/cases/go-sql-injection/label.json rename to benchmarks/public-catch-rate/cases/go-sql-injection/label.json diff --git a/benchmark/cases/go-sql-injection/source.go b/benchmarks/public-catch-rate/cases/go-sql-injection/source.go similarity index 100% rename from benchmark/cases/go-sql-injection/source.go rename to benchmarks/public-catch-rate/cases/go-sql-injection/source.go diff --git a/benchmark/cases/java-insecure-random/label.json b/benchmarks/public-catch-rate/cases/java-insecure-random/label.json similarity index 100% rename from benchmark/cases/java-insecure-random/label.json rename to benchmarks/public-catch-rate/cases/java-insecure-random/label.json diff --git a/benchmark/cases/java-insecure-random/source.java b/benchmarks/public-catch-rate/cases/java-insecure-random/source.java similarity index 100% rename from benchmark/cases/java-insecure-random/source.java rename to benchmarks/public-catch-rate/cases/java-insecure-random/source.java diff --git a/benchmark/cases/js-eval-injection/label.json b/benchmarks/public-catch-rate/cases/js-eval-injection/label.json similarity index 100% rename from benchmark/cases/js-eval-injection/label.json rename to benchmarks/public-catch-rate/cases/js-eval-injection/label.json diff --git a/benchmark/cases/js-eval-injection/source.js b/benchmarks/public-catch-rate/cases/js-eval-injection/source.js similarity index 100% rename from benchmark/cases/js-eval-injection/source.js rename to benchmarks/public-catch-rate/cases/js-eval-injection/source.js diff --git a/benchmark/cases/js-open-redirect/label.json b/benchmarks/public-catch-rate/cases/js-open-redirect/label.json similarity index 100% rename from benchmark/cases/js-open-redirect/label.json rename to benchmarks/public-catch-rate/cases/js-open-redirect/label.json diff --git a/benchmark/cases/js-open-redirect/source.js b/benchmarks/public-catch-rate/cases/js-open-redirect/source.js similarity index 100% rename from benchmark/cases/js-open-redirect/source.js rename to benchmarks/public-catch-rate/cases/js-open-redirect/source.js diff --git a/benchmark/cases/py-bare-except/label.json b/benchmarks/public-catch-rate/cases/py-bare-except/label.json similarity index 100% rename from benchmark/cases/py-bare-except/label.json rename to benchmarks/public-catch-rate/cases/py-bare-except/label.json diff --git a/benchmark/cases/py-bare-except/source.py b/benchmarks/public-catch-rate/cases/py-bare-except/source.py similarity index 100% rename from benchmark/cases/py-bare-except/source.py rename to benchmarks/public-catch-rate/cases/py-bare-except/source.py diff --git a/benchmark/cases/py-command-injection/label.json b/benchmarks/public-catch-rate/cases/py-command-injection/label.json similarity index 100% rename from benchmark/cases/py-command-injection/label.json rename to benchmarks/public-catch-rate/cases/py-command-injection/label.json diff --git a/benchmark/cases/py-command-injection/source.py b/benchmarks/public-catch-rate/cases/py-command-injection/source.py similarity index 100% rename from benchmark/cases/py-command-injection/source.py rename to benchmarks/public-catch-rate/cases/py-command-injection/source.py diff --git a/benchmark/cases/py-hardcoded-secret/label.json b/benchmarks/public-catch-rate/cases/py-hardcoded-secret/label.json similarity index 100% rename from benchmark/cases/py-hardcoded-secret/label.json rename to benchmarks/public-catch-rate/cases/py-hardcoded-secret/label.json diff --git a/benchmark/cases/py-hardcoded-secret/source.py b/benchmarks/public-catch-rate/cases/py-hardcoded-secret/source.py similarity index 100% rename from benchmark/cases/py-hardcoded-secret/source.py rename to benchmarks/public-catch-rate/cases/py-hardcoded-secret/source.py diff --git a/benchmark/cases/py-insecure-deserialization/label.json b/benchmarks/public-catch-rate/cases/py-insecure-deserialization/label.json similarity index 100% rename from benchmark/cases/py-insecure-deserialization/label.json rename to benchmarks/public-catch-rate/cases/py-insecure-deserialization/label.json diff --git a/benchmark/cases/py-insecure-deserialization/source.py b/benchmarks/public-catch-rate/cases/py-insecure-deserialization/source.py similarity index 100% rename from benchmark/cases/py-insecure-deserialization/source.py rename to benchmarks/public-catch-rate/cases/py-insecure-deserialization/source.py diff --git a/benchmark/cases/py-path-traversal/label.json b/benchmarks/public-catch-rate/cases/py-path-traversal/label.json similarity index 100% rename from benchmark/cases/py-path-traversal/label.json rename to benchmarks/public-catch-rate/cases/py-path-traversal/label.json diff --git a/benchmark/cases/py-path-traversal/source.py b/benchmarks/public-catch-rate/cases/py-path-traversal/source.py similarity index 100% rename from benchmark/cases/py-path-traversal/source.py rename to benchmarks/public-catch-rate/cases/py-path-traversal/source.py diff --git a/benchmark/cases/py-sql-injection/label.json b/benchmarks/public-catch-rate/cases/py-sql-injection/label.json similarity index 100% rename from benchmark/cases/py-sql-injection/label.json rename to benchmarks/public-catch-rate/cases/py-sql-injection/label.json diff --git a/benchmark/cases/py-sql-injection/source.py b/benchmarks/public-catch-rate/cases/py-sql-injection/source.py similarity index 100% rename from benchmark/cases/py-sql-injection/source.py rename to benchmarks/public-catch-rate/cases/py-sql-injection/source.py diff --git a/benchmark/cases/py-ssrf/label.json b/benchmarks/public-catch-rate/cases/py-ssrf/label.json similarity index 100% rename from benchmark/cases/py-ssrf/label.json rename to benchmarks/public-catch-rate/cases/py-ssrf/label.json diff --git a/benchmark/cases/py-ssrf/source.py b/benchmarks/public-catch-rate/cases/py-ssrf/source.py similarity index 100% rename from benchmark/cases/py-ssrf/source.py rename to benchmarks/public-catch-rate/cases/py-ssrf/source.py diff --git a/benchmark/cases/py-weak-hash/label.json b/benchmarks/public-catch-rate/cases/py-weak-hash/label.json similarity index 100% rename from benchmark/cases/py-weak-hash/label.json rename to benchmarks/public-catch-rate/cases/py-weak-hash/label.json diff --git a/benchmark/cases/py-weak-hash/source.py b/benchmarks/public-catch-rate/cases/py-weak-hash/source.py similarity index 100% rename from benchmark/cases/py-weak-hash/source.py rename to benchmarks/public-catch-rate/cases/py-weak-hash/source.py diff --git a/benchmark/cases/py-zip-bomb/label.json b/benchmarks/public-catch-rate/cases/py-zip-bomb/label.json similarity index 100% rename from benchmark/cases/py-zip-bomb/label.json rename to benchmarks/public-catch-rate/cases/py-zip-bomb/label.json diff --git a/benchmark/cases/py-zip-bomb/source.py b/benchmarks/public-catch-rate/cases/py-zip-bomb/source.py similarity index 100% rename from benchmark/cases/py-zip-bomb/source.py rename to benchmarks/public-catch-rate/cases/py-zip-bomb/source.py diff --git a/benchmark/cases/rust-integer-overflow/label.json b/benchmarks/public-catch-rate/cases/rust-integer-overflow/label.json similarity index 100% rename from benchmark/cases/rust-integer-overflow/label.json rename to benchmarks/public-catch-rate/cases/rust-integer-overflow/label.json diff --git a/benchmark/cases/rust-integer-overflow/source.rs b/benchmarks/public-catch-rate/cases/rust-integer-overflow/source.rs similarity index 100% rename from benchmark/cases/rust-integer-overflow/source.rs rename to benchmarks/public-catch-rate/cases/rust-integer-overflow/source.rs diff --git a/benchmark/cases/ts-dead-code/label.json b/benchmarks/public-catch-rate/cases/ts-dead-code/label.json similarity index 100% rename from benchmark/cases/ts-dead-code/label.json rename to benchmarks/public-catch-rate/cases/ts-dead-code/label.json diff --git a/benchmark/cases/ts-dead-code/source.ts b/benchmarks/public-catch-rate/cases/ts-dead-code/source.ts similarity index 100% rename from benchmark/cases/ts-dead-code/source.ts rename to benchmarks/public-catch-rate/cases/ts-dead-code/source.ts diff --git a/benchmark/cases/ts-hardcoded-credentials/label.json b/benchmarks/public-catch-rate/cases/ts-hardcoded-credentials/label.json similarity index 100% rename from benchmark/cases/ts-hardcoded-credentials/label.json rename to benchmarks/public-catch-rate/cases/ts-hardcoded-credentials/label.json diff --git a/benchmark/cases/ts-hardcoded-credentials/source.ts b/benchmarks/public-catch-rate/cases/ts-hardcoded-credentials/source.ts similarity index 100% rename from benchmark/cases/ts-hardcoded-credentials/source.ts rename to benchmarks/public-catch-rate/cases/ts-hardcoded-credentials/source.ts diff --git a/benchmark/cases/ts-insecure-cookie/label.json b/benchmarks/public-catch-rate/cases/ts-insecure-cookie/label.json similarity index 100% rename from benchmark/cases/ts-insecure-cookie/label.json rename to benchmarks/public-catch-rate/cases/ts-insecure-cookie/label.json diff --git a/benchmark/cases/ts-insecure-cookie/source.ts b/benchmarks/public-catch-rate/cases/ts-insecure-cookie/source.ts similarity index 100% rename from benchmark/cases/ts-insecure-cookie/source.ts rename to benchmarks/public-catch-rate/cases/ts-insecure-cookie/source.ts diff --git a/benchmark/cases/ts-missing-await/label.json b/benchmarks/public-catch-rate/cases/ts-missing-await/label.json similarity index 100% rename from benchmark/cases/ts-missing-await/label.json rename to benchmarks/public-catch-rate/cases/ts-missing-await/label.json diff --git a/benchmark/cases/ts-missing-await/source.ts b/benchmarks/public-catch-rate/cases/ts-missing-await/source.ts similarity index 100% rename from benchmark/cases/ts-missing-await/source.ts rename to benchmarks/public-catch-rate/cases/ts-missing-await/source.ts diff --git a/benchmark/cases/ts-prototype-pollution/label.json b/benchmarks/public-catch-rate/cases/ts-prototype-pollution/label.json similarity index 100% rename from benchmark/cases/ts-prototype-pollution/label.json rename to benchmarks/public-catch-rate/cases/ts-prototype-pollution/label.json diff --git a/benchmark/cases/ts-prototype-pollution/source.ts b/benchmarks/public-catch-rate/cases/ts-prototype-pollution/source.ts similarity index 100% rename from benchmark/cases/ts-prototype-pollution/source.ts rename to benchmarks/public-catch-rate/cases/ts-prototype-pollution/source.ts diff --git a/benchmark/cases/ts-regex-dos/label.json b/benchmarks/public-catch-rate/cases/ts-regex-dos/label.json similarity index 100% rename from benchmark/cases/ts-regex-dos/label.json rename to benchmarks/public-catch-rate/cases/ts-regex-dos/label.json diff --git a/benchmark/cases/ts-regex-dos/source.ts b/benchmarks/public-catch-rate/cases/ts-regex-dos/source.ts similarity index 100% rename from benchmark/cases/ts-regex-dos/source.ts rename to benchmarks/public-catch-rate/cases/ts-regex-dos/source.ts diff --git a/benchmark/cases/ts-sql-injection/label.json b/benchmarks/public-catch-rate/cases/ts-sql-injection/label.json similarity index 100% rename from benchmark/cases/ts-sql-injection/label.json rename to benchmarks/public-catch-rate/cases/ts-sql-injection/label.json diff --git a/benchmark/cases/ts-sql-injection/source.ts b/benchmarks/public-catch-rate/cases/ts-sql-injection/source.ts similarity index 100% rename from benchmark/cases/ts-sql-injection/source.ts rename to benchmarks/public-catch-rate/cases/ts-sql-injection/source.ts diff --git a/benchmark/cases/ts-type-confusion/label.json b/benchmarks/public-catch-rate/cases/ts-type-confusion/label.json similarity index 100% rename from benchmark/cases/ts-type-confusion/label.json rename to benchmarks/public-catch-rate/cases/ts-type-confusion/label.json diff --git a/benchmark/cases/ts-type-confusion/source.ts b/benchmarks/public-catch-rate/cases/ts-type-confusion/source.ts similarity index 100% rename from benchmark/cases/ts-type-confusion/source.ts rename to benchmarks/public-catch-rate/cases/ts-type-confusion/source.ts diff --git a/benchmark/cases/ts-xss/label.json b/benchmarks/public-catch-rate/cases/ts-xss/label.json similarity index 100% rename from benchmark/cases/ts-xss/label.json rename to benchmarks/public-catch-rate/cases/ts-xss/label.json diff --git a/benchmark/cases/ts-xss/source.tsx b/benchmarks/public-catch-rate/cases/ts-xss/source.tsx similarity index 100% rename from benchmark/cases/ts-xss/source.tsx rename to benchmarks/public-catch-rate/cases/ts-xss/source.tsx diff --git a/benchmark/reviews-raw/go-errcheck.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/go-errcheck.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/go-errcheck.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/go-errcheck.codevetter.raw.json diff --git a/benchmark/reviews-raw/go-hardcoded-credentials.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/go-hardcoded-credentials.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/go-hardcoded-credentials.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/go-hardcoded-credentials.codevetter.raw.json diff --git a/benchmark/reviews-raw/go-nil-pointer.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/go-nil-pointer.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/go-nil-pointer.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/go-nil-pointer.codevetter.raw.json diff --git a/benchmark/reviews-raw/go-race-condition.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/go-race-condition.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/go-race-condition.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/go-race-condition.codevetter.raw.json diff --git a/benchmark/reviews-raw/go-sql-injection.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/go-sql-injection.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/go-sql-injection.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/go-sql-injection.codevetter.raw.json diff --git a/benchmark/reviews-raw/java-insecure-random.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/java-insecure-random.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/java-insecure-random.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/java-insecure-random.codevetter.raw.json diff --git a/benchmark/reviews-raw/js-eval-injection.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/js-eval-injection.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/js-eval-injection.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/js-eval-injection.codevetter.raw.json diff --git a/benchmark/reviews-raw/js-open-redirect.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/js-open-redirect.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/js-open-redirect.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/js-open-redirect.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-bare-except.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-bare-except.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-bare-except.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-bare-except.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-command-injection.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-command-injection.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-command-injection.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-command-injection.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-hardcoded-secret.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-hardcoded-secret.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-hardcoded-secret.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-hardcoded-secret.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-insecure-deserialization.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-insecure-deserialization.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-insecure-deserialization.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-insecure-deserialization.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-path-traversal.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-path-traversal.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-path-traversal.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-path-traversal.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-sql-injection.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-sql-injection.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-sql-injection.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-sql-injection.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-ssrf.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-ssrf.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-ssrf.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-ssrf.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-weak-hash.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-weak-hash.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-weak-hash.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-weak-hash.codevetter.raw.json diff --git a/benchmark/reviews-raw/py-zip-bomb.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/py-zip-bomb.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/py-zip-bomb.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/py-zip-bomb.codevetter.raw.json diff --git a/benchmark/reviews-raw/rust-integer-overflow.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/rust-integer-overflow.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/rust-integer-overflow.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/rust-integer-overflow.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-dead-code.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-dead-code.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-dead-code.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-dead-code.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-hardcoded-credentials.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-hardcoded-credentials.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-hardcoded-credentials.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-hardcoded-credentials.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-insecure-cookie.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-insecure-cookie.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-insecure-cookie.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-insecure-cookie.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-missing-await.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-missing-await.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-missing-await.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-missing-await.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-prototype-pollution.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-prototype-pollution.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-prototype-pollution.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-prototype-pollution.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-regex-dos.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-regex-dos.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-regex-dos.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-regex-dos.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-sql-injection.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-sql-injection.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-sql-injection.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-sql-injection.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-type-confusion.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-type-confusion.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-type-confusion.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-type-confusion.codevetter.raw.json diff --git a/benchmark/reviews-raw/ts-xss.codevetter.raw.json b/benchmarks/public-catch-rate/reviews-raw/ts-xss.codevetter.raw.json similarity index 100% rename from benchmark/reviews-raw/ts-xss.codevetter.raw.json rename to benchmarks/public-catch-rate/reviews-raw/ts-xss.codevetter.raw.json diff --git a/biome.json b/biome.json index 5fdb8c3a..899fac06 100644 --- a/biome.json +++ b/biome.json @@ -36,7 +36,7 @@ "!**/*.css", "!**/*.svg", "!**/*.astro", - "!benchmark/cases" + "!benchmarks/public-catch-rate/cases" ] }, "formatter": { diff --git a/docs/development/benchmark.md b/docs/development/benchmark.md index 146c1a26..62e5351c 100644 --- a/docs/development/benchmark.md +++ b/docs/development/benchmark.md @@ -95,7 +95,7 @@ avoid overfitting 27 cases. Protocol: every case ran through the REAL production pipeline (risk tiers, specialists, coordinator, dedup) via the ignored Rust harness `diag_benchmark_generate_codevetter_reviews`; raw outputs live in -`benchmark/reviews-raw/`. Ground-truth mapping was proposed mechanically +`benchmarks/public-catch-rate/reviews-raw/`. Ground-truth mapping was proposed mechanically (`scripts/map-benchmark-reviews.mjs`) and then hand-judged per finding under one rule: a finding matches only if its CORE CLAIM identifies the defect; process findings (no-tests, shipped-with-comment) never match. diff --git a/docs/operations/automation-contract.md b/docs/operations/automation-contract.md index 29aad18a..5f234e4e 100644 --- a/docs/operations/automation-contract.md +++ b/docs/operations/automation-contract.md @@ -34,7 +34,7 @@ Do not duplicate this matrix elsewhere — link here. | Desktop Rust backend | `apps/desktop/src-tauri/src/` | `ci.yml` MCP tests; `release.yml` Tauri build | Sarthak | | Local SQLite | `rusqlite` in Rust backend (no server) | Local only; `observability.rs` aggregates locally | Sarthak | | MCP sidecar | `apps/desktop/src-tauri/src/bin/codevetter-mcp.rs` | `ci.yml` MCP protocol + stdio lifecycle tests; `mcp/sanitize.rs` redaction | Sarthak | -| Benchmark | `benchmark/` + `scripts/run-catch-rate-benchmark.mjs` | `pnpm test:benchmark`; public cases committed | Sarthak | +| Benchmark | `benchmarks/public-catch-rate/` + `scripts/run-catch-rate-benchmark.mjs` | `pnpm test:benchmark`; public cases committed | Sarthak | | Release pipeline | `auto-release.yml` → `release.yml` → GitHub Releases | Release assets + `latest.json` manifest | Sarthak | | Auto-updater | `@tauri-apps/plugin-updater` consuming `latest.json` | `scripts/verify-release-manifest.mjs` validates linkage | Sarthak | | Docs | `docs/` + `docs-site/` (Blume) | `docs.yml` link + structure validation | Sarthak | diff --git a/docs/product/overview.md b/docs/product/overview.md index 0def6952..6181e561 100644 --- a/docs/product/overview.md +++ b/docs/product/overview.md @@ -56,7 +56,7 @@ work, and did the affected audience succeed with it?* ## Benchmark evidence -27 hand-labeled public benchmark cases (`benchmark/cases/`) covering 7 +27 hand-labeled public benchmark cases (`benchmarks/public-catch-rate/cases/`) covering 7 languages and 15+ vulnerability types. The coordinator dedup fix (2026-07-11) flipped the head-to-head vs raw Claude: catch 1.000 vs 0.931, precision 0.433 vs 0.397, F1 0.604 vs 0.557. Real agent-PR case curation diff --git a/scripts/generate-benchmark-dataset.mjs b/scripts/generate-benchmark-dataset.mjs index dc9ea3da..92508c66 100644 --- a/scripts/generate-benchmark-dataset.mjs +++ b/scripts/generate-benchmark-dataset.mjs @@ -14,7 +14,7 @@ import fs from 'node:fs'; import path from 'node:path'; const ROOT = process.cwd(); -const CASES_DIR = path.join(ROOT, 'benchmark/cases'); +const CASES_DIR = path.join(ROOT, 'benchmarks/public-catch-rate/cases'); const LANDING_PUBLIC = path.join(ROOT, 'apps/landing-page-astro/public'); const LANDING_DATA = path.join(ROOT, 'apps/landing-page-astro/src/data'); diff --git a/scripts/map-benchmark-reviews.mjs b/scripts/map-benchmark-reviews.mjs index 8082c84c..e6d67492 100644 --- a/scripts/map-benchmark-reviews.mjs +++ b/scripts/map-benchmark-reviews.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node -// Convert raw CodeVetter pipeline output (benchmark/reviews-raw/.codevetter.raw.json, +// Convert raw CodeVetter pipeline output (benchmarks/public-catch-rate/reviews-raw/.codevetter.raw.json, // produced by the diag_benchmark_generate_codevetter_reviews harness) into the -// scorer's reviewer format at benchmark/reviews/.codevetter.json. +// scorer's reviewer format at benchmarks/public-catch-rate/reviews/.codevetter.json. // // Ground-truth matching here is a mechanical PROPOSAL (line overlap + keyword // correspondence); every proposed file is meant to be hand-checked before diff --git a/scripts/run-public-benchmark.mjs b/scripts/run-public-benchmark.mjs index a60498b3..e9600a4e 100644 --- a/scripts/run-public-benchmark.mjs +++ b/scripts/run-public-benchmark.mjs @@ -1,8 +1,8 @@ #!/usr/bin/env node -// Public benchmark scorer for benchmark/cases/*. +// Public benchmark scorer for benchmarks/public-catch-rate/cases/*. // // Validates every hand-labeled case and, when reviewer output files are -// present in benchmark/reviews/.json, computes catch-rate, precision, +// present in benchmarks/public-catch-rate/reviews/.json, computes catch-rate, precision, // F1, false-positive, redundant-match, and per-severity metrics. // // Usage: @@ -14,8 +14,8 @@ import fs from 'node:fs'; import path from 'node:path'; -const CASES_DIR = path.resolve(process.cwd(), 'benchmark/cases'); -const REVIEWS_DIR = path.resolve(process.cwd(), 'benchmark/reviews'); +const CASES_DIR = path.resolve(process.cwd(), 'benchmarks/public-catch-rate/cases'); +const REVIEWS_DIR = path.resolve(process.cwd(), 'benchmarks/public-catch-rate/reviews'); const SEVERITY_RANK = { low: 1, medium: 2, high: 3, critical: 4 }; @@ -320,7 +320,7 @@ function main() { process.exit(1); } if (!cases.length) { - console.error('No benchmark cases found under benchmark/cases'); + console.error('No benchmark cases found under benchmarks/public-catch-rate/cases'); process.exit(1); } const reviews = loadReviews(args.reviewer); From 5bd34270ef9775a57305171c6d9f232f4ae5ca2a Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 13:40:37 +0530 Subject: [PATCH 04/37] refactor(evidence): consolidate committed evidence into one evidence/ root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evidence was spread across four locations: artifacts/, .fleet/evidence/, .fleet/design-reviews/, and apps/desktop/artifacts/. The split was actively lossy — .fleet/evidence/landing-audit/ held the scorecard and performance report while artifacts/design/landing-audit/ held the screenshots of the same audit. They now sit together. artifacts/ also had two incompatible jobs: committed design evidence, and the scratch output path the docs tell users to write to (--out artifacts/... in docs/development/verification-receipts.md, benchmark.md and performance.md). Splitting them resolves that — artifacts/ is now gitignored scratch, and those existing doc examples become correct rather than ambiguous. Layout: evidence/design/ screenshots, landing-audit, changelog, probes, reviews evidence/performance/ performance-lab results and manifests evidence/reviews/ PR review captures All moves use git mv so rename history survives. Co-Authored-By: Claude Opus 5 (1M context) --- docs/knowledge/performance-lab-publication.md | 2 +- {artifacts => evidence}/design/after-1440.png | Bin {artifacts => evidence}/design/after-390.png | Bin {artifacts => evidence}/design/after-768.png | Bin .../design}/changelog/after-1440.png | Bin .../design}/changelog/after-390.png | Bin .../design}/changelog/after-768.png | Bin .../design}/changelog/before-1440.png | Bin .../design/content-cluster-after-1440.png | Bin .../design/content-cluster-after-390.png | Bin .../design/content-cluster-after-768.png | Bin .../design/content-cluster-before.png | Bin .../design/crash-recovery-after-1440.png | Bin .../design/crash-recovery-after-390.png | Bin .../design/crash-recovery-after-768.png | Bin .../design/crash-recovery-before-1440.png | Bin .../design/crash-recovery-final-1440.jpg | Bin .../design/crash-recovery-final-390.jpg | Bin .../design/crash-recovery-final-768.jpg | Bin {artifacts => evidence}/design/design-review.json | 0 .../design/landing-audit/candidate-1440.png | Bin .../design/landing-audit/candidate-390.png | Bin .../design/landing-audit/candidate-768.png | Bin .../design/landing-audit/final-1440.png | Bin .../design/landing-audit/final-390.png | Bin .../design/landing-audit/final-768.png | Bin .../design/landing-audit/final-p1-1440.png | Bin .../design/landing-audit/final-p1-390.png | Bin .../design/landing-audit/final-p1-768.png | Bin .../design}/landing-audit/performance.html | 0 .../design/landing-audit/rendered-1440.png | Bin .../design/landing-audit/rendered-390.png | Bin .../design/landing-audit/rendered-768.png | Bin .../design}/landing-audit/scorecard.json | 0 .../design/landing-audit/scrolled-390.png | Bin .../design/probes/review-overhaul-directions.html | 0 .../probes/review-overhaul-evidence-brief.png | Bin .../design/probes/review-overhaul-inline-review.png | Bin .../design/probes/review-overhaul-triage-desk.png | Bin .../design/product-surfaces-after-1440.jpg | Bin .../design/product-surfaces-after-390.jpg | Bin .../design/product-surfaces-after-768.jpg | Bin .../design/product-surfaces-before-1440.jpg | Bin .../design/product-surfaces-scope-1440.png | Bin .../design/product-surfaces-scope-390.png | Bin .../design/product-surfaces-scope-768.png | Bin .../design/review-agent-readiness-after-1440.png | Bin .../design/review-agent-readiness-after-390.png | Bin .../design/review-agent-readiness-after-768.png | Bin .../design/review-desktop-after-1440.png | Bin .../design/review-desktop-final-1440.png | Bin .../design/review-inline-overhaul-after-1280.png | Bin .../design/review-inline-overhaul-after-1440.png | Bin .../design/review-inline-overhaul-after-900.png | Bin .../design/review-inline-overhaul-audit-390.png | Bin .../design/review-inline-overhaul-audit-768.png | Bin .../design/review-inspector-readable-after-1440.png | Bin .../design/review-navigation-after-1440.png | Bin .../design/review-result-polish-after-1440.png | Bin .../design/review-result-polish-after-390.png | Bin .../design/review-result-polish-after-768.png | Bin .../design/reviews}/crash-recovery.json | 0 .../design/reviews}/desktop-navigation-clarity.json | 0 .../reviews}/focus-desktop-product-surfaces.json | 0 .../design/reviews}/landing-page.json | 0 .../local-review-test-performance-check.json | 0 .../reviews}/review-inspector-readability.json | 0 .../design/reviews}/review-result-polish.json | 0 .../design/reviews/review-workbench-overhaul.json | 0 .../design/reviews}/sidebar.json | 0 .../trex-change-and-preview-verification.json | 0 .../design/sidebar-after-1440.png | Bin .../design/sidebar-after-390.png | Bin .../design/sidebar-after-768.png | Bin .../design/structural-context-evaluation.html | 0 {artifacts => evidence}/design/trex-after-1440.png | Bin {artifacts => evidence}/design/trex-after-390.png | Bin {artifacts => evidence}/design/trex-after-768.png | Bin {artifacts => evidence}/design/trex-before-1440.png | Bin {artifacts => evidence}/design/trex-result-1440.png | Bin .../design/usage-telemetry-after-1440.png | Bin .../design/usage-telemetry-after-390.png | Bin .../design/usage-telemetry-after-768.png | Bin .../design/usage-telemetry-before-1440.png | Bin .../calorie-exercise-guidance-2026-08-10.md | 0 .../performance/old-local-portfolio-manifest.json | 0 .../old-local-projects-results-2026-08-09.md | 0 .../reviews/pr157-codevetter-review.png | Bin .../reviews/pr173-codevetter-review.png | Bin 89 files changed, 1 insertion(+), 1 deletion(-) rename {artifacts => evidence}/design/after-1440.png (100%) rename {artifacts => evidence}/design/after-390.png (100%) rename {artifacts => evidence}/design/after-768.png (100%) rename {.fleet/evidence => evidence/design}/changelog/after-1440.png (100%) rename {.fleet/evidence => evidence/design}/changelog/after-390.png (100%) rename {.fleet/evidence => evidence/design}/changelog/after-768.png (100%) rename {.fleet/evidence => evidence/design}/changelog/before-1440.png (100%) rename {artifacts => evidence}/design/content-cluster-after-1440.png (100%) rename {artifacts => evidence}/design/content-cluster-after-390.png (100%) rename {artifacts => evidence}/design/content-cluster-after-768.png (100%) rename {artifacts => evidence}/design/content-cluster-before.png (100%) rename {artifacts => evidence}/design/crash-recovery-after-1440.png (100%) rename {artifacts => evidence}/design/crash-recovery-after-390.png (100%) rename {artifacts => evidence}/design/crash-recovery-after-768.png (100%) rename {artifacts => evidence}/design/crash-recovery-before-1440.png (100%) rename {artifacts => evidence}/design/crash-recovery-final-1440.jpg (100%) rename {artifacts => evidence}/design/crash-recovery-final-390.jpg (100%) rename {artifacts => evidence}/design/crash-recovery-final-768.jpg (100%) rename {artifacts => evidence}/design/design-review.json (100%) rename {artifacts => evidence}/design/landing-audit/candidate-1440.png (100%) rename {artifacts => evidence}/design/landing-audit/candidate-390.png (100%) rename {artifacts => evidence}/design/landing-audit/candidate-768.png (100%) rename {artifacts => evidence}/design/landing-audit/final-1440.png (100%) rename {artifacts => evidence}/design/landing-audit/final-390.png (100%) rename {artifacts => evidence}/design/landing-audit/final-768.png (100%) rename {artifacts => evidence}/design/landing-audit/final-p1-1440.png (100%) rename {artifacts => evidence}/design/landing-audit/final-p1-390.png (100%) rename {artifacts => evidence}/design/landing-audit/final-p1-768.png (100%) rename {.fleet/evidence => evidence/design}/landing-audit/performance.html (100%) rename {artifacts => evidence}/design/landing-audit/rendered-1440.png (100%) rename {artifacts => evidence}/design/landing-audit/rendered-390.png (100%) rename {artifacts => evidence}/design/landing-audit/rendered-768.png (100%) rename {.fleet/evidence => evidence/design}/landing-audit/scorecard.json (100%) rename {artifacts => evidence}/design/landing-audit/scrolled-390.png (100%) rename {artifacts => evidence}/design/probes/review-overhaul-directions.html (100%) rename {artifacts => evidence}/design/probes/review-overhaul-evidence-brief.png (100%) rename {artifacts => evidence}/design/probes/review-overhaul-inline-review.png (100%) rename {artifacts => evidence}/design/probes/review-overhaul-triage-desk.png (100%) rename {artifacts => evidence}/design/product-surfaces-after-1440.jpg (100%) rename {artifacts => evidence}/design/product-surfaces-after-390.jpg (100%) rename {artifacts => evidence}/design/product-surfaces-after-768.jpg (100%) rename {artifacts => evidence}/design/product-surfaces-before-1440.jpg (100%) rename {apps/desktop/artifacts => evidence}/design/product-surfaces-scope-1440.png (100%) rename {apps/desktop/artifacts => evidence}/design/product-surfaces-scope-390.png (100%) rename {apps/desktop/artifacts => evidence}/design/product-surfaces-scope-768.png (100%) rename {artifacts => evidence}/design/review-agent-readiness-after-1440.png (100%) rename {artifacts => evidence}/design/review-agent-readiness-after-390.png (100%) rename {artifacts => evidence}/design/review-agent-readiness-after-768.png (100%) rename {artifacts => evidence}/design/review-desktop-after-1440.png (100%) rename {artifacts => evidence}/design/review-desktop-final-1440.png (100%) rename {artifacts => evidence}/design/review-inline-overhaul-after-1280.png (100%) rename {artifacts => evidence}/design/review-inline-overhaul-after-1440.png (100%) rename {artifacts => evidence}/design/review-inline-overhaul-after-900.png (100%) rename {artifacts => evidence}/design/review-inline-overhaul-audit-390.png (100%) rename {artifacts => evidence}/design/review-inline-overhaul-audit-768.png (100%) rename {artifacts => evidence}/design/review-inspector-readable-after-1440.png (100%) rename {artifacts => evidence}/design/review-navigation-after-1440.png (100%) rename {artifacts => evidence}/design/review-result-polish-after-1440.png (100%) rename {artifacts => evidence}/design/review-result-polish-after-390.png (100%) rename {artifacts => evidence}/design/review-result-polish-after-768.png (100%) rename {.fleet/design-reviews => evidence/design/reviews}/crash-recovery.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/desktop-navigation-clarity.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/focus-desktop-product-surfaces.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/landing-page.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/local-review-test-performance-check.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/review-inspector-readability.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/review-result-polish.json (100%) rename .fleet/design-review.json => evidence/design/reviews/review-workbench-overhaul.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/sidebar.json (100%) rename {.fleet/design-reviews => evidence/design/reviews}/trex-change-and-preview-verification.json (100%) rename {artifacts => evidence}/design/sidebar-after-1440.png (100%) rename {artifacts => evidence}/design/sidebar-after-390.png (100%) rename {artifacts => evidence}/design/sidebar-after-768.png (100%) rename {artifacts => evidence}/design/structural-context-evaluation.html (100%) rename {artifacts => evidence}/design/trex-after-1440.png (100%) rename {artifacts => evidence}/design/trex-after-390.png (100%) rename {artifacts => evidence}/design/trex-after-768.png (100%) rename {artifacts => evidence}/design/trex-before-1440.png (100%) rename {artifacts => evidence}/design/trex-result-1440.png (100%) rename {artifacts => evidence}/design/usage-telemetry-after-1440.png (100%) rename {artifacts => evidence}/design/usage-telemetry-after-390.png (100%) rename {artifacts => evidence}/design/usage-telemetry-after-768.png (100%) rename {artifacts => evidence}/design/usage-telemetry-before-1440.png (100%) rename {artifacts => evidence}/performance/calorie-exercise-guidance-2026-08-10.md (100%) rename {artifacts => evidence}/performance/old-local-portfolio-manifest.json (100%) rename {artifacts => evidence}/performance/old-local-projects-results-2026-08-09.md (100%) rename {artifacts => evidence}/reviews/pr157-codevetter-review.png (100%) rename {artifacts => evidence}/reviews/pr173-codevetter-review.png (100%) diff --git a/docs/knowledge/performance-lab-publication.md b/docs/knowledge/performance-lab-publication.md index b8a35f89..c40103c1 100644 --- a/docs/knowledge/performance-lab-publication.md +++ b/docs/knowledge/performance-lab-publication.md @@ -55,7 +55,7 @@ product's end game. Merge status does not upgrade evidence strength: the directional, unverified, guardrail, and synthetic-stress entries are not independent claims of proven customer impact. The full inactive-project disposition is retained in -[`artifacts/performance/old-local-projects-results-2026-08-09.md`](../../artifacts/performance/old-local-projects-results-2026-08-09.md). +[`evidence/performance/old-local-projects-results-2026-08-09.md`](../../evidence/performance/old-local-projects-results-2026-08-09.md). ## External open-source work diff --git a/artifacts/design/after-1440.png b/evidence/design/after-1440.png similarity index 100% rename from artifacts/design/after-1440.png rename to evidence/design/after-1440.png diff --git a/artifacts/design/after-390.png b/evidence/design/after-390.png similarity index 100% rename from artifacts/design/after-390.png rename to evidence/design/after-390.png diff --git a/artifacts/design/after-768.png b/evidence/design/after-768.png similarity index 100% rename from artifacts/design/after-768.png rename to evidence/design/after-768.png diff --git a/.fleet/evidence/changelog/after-1440.png b/evidence/design/changelog/after-1440.png similarity index 100% rename from .fleet/evidence/changelog/after-1440.png rename to evidence/design/changelog/after-1440.png diff --git a/.fleet/evidence/changelog/after-390.png b/evidence/design/changelog/after-390.png similarity index 100% rename from .fleet/evidence/changelog/after-390.png rename to evidence/design/changelog/after-390.png diff --git a/.fleet/evidence/changelog/after-768.png b/evidence/design/changelog/after-768.png similarity index 100% rename from .fleet/evidence/changelog/after-768.png rename to evidence/design/changelog/after-768.png diff --git a/.fleet/evidence/changelog/before-1440.png b/evidence/design/changelog/before-1440.png similarity index 100% rename from .fleet/evidence/changelog/before-1440.png rename to evidence/design/changelog/before-1440.png diff --git a/artifacts/design/content-cluster-after-1440.png b/evidence/design/content-cluster-after-1440.png similarity index 100% rename from artifacts/design/content-cluster-after-1440.png rename to evidence/design/content-cluster-after-1440.png diff --git a/artifacts/design/content-cluster-after-390.png b/evidence/design/content-cluster-after-390.png similarity index 100% rename from artifacts/design/content-cluster-after-390.png rename to evidence/design/content-cluster-after-390.png diff --git a/artifacts/design/content-cluster-after-768.png b/evidence/design/content-cluster-after-768.png similarity index 100% rename from artifacts/design/content-cluster-after-768.png rename to evidence/design/content-cluster-after-768.png diff --git a/artifacts/design/content-cluster-before.png b/evidence/design/content-cluster-before.png similarity index 100% rename from artifacts/design/content-cluster-before.png rename to evidence/design/content-cluster-before.png diff --git a/artifacts/design/crash-recovery-after-1440.png b/evidence/design/crash-recovery-after-1440.png similarity index 100% rename from artifacts/design/crash-recovery-after-1440.png rename to evidence/design/crash-recovery-after-1440.png diff --git a/artifacts/design/crash-recovery-after-390.png b/evidence/design/crash-recovery-after-390.png similarity index 100% rename from artifacts/design/crash-recovery-after-390.png rename to evidence/design/crash-recovery-after-390.png diff --git a/artifacts/design/crash-recovery-after-768.png b/evidence/design/crash-recovery-after-768.png similarity index 100% rename from artifacts/design/crash-recovery-after-768.png rename to evidence/design/crash-recovery-after-768.png diff --git a/artifacts/design/crash-recovery-before-1440.png b/evidence/design/crash-recovery-before-1440.png similarity index 100% rename from artifacts/design/crash-recovery-before-1440.png rename to evidence/design/crash-recovery-before-1440.png diff --git a/artifacts/design/crash-recovery-final-1440.jpg b/evidence/design/crash-recovery-final-1440.jpg similarity index 100% rename from artifacts/design/crash-recovery-final-1440.jpg rename to evidence/design/crash-recovery-final-1440.jpg diff --git a/artifacts/design/crash-recovery-final-390.jpg b/evidence/design/crash-recovery-final-390.jpg similarity index 100% rename from artifacts/design/crash-recovery-final-390.jpg rename to evidence/design/crash-recovery-final-390.jpg diff --git a/artifacts/design/crash-recovery-final-768.jpg b/evidence/design/crash-recovery-final-768.jpg similarity index 100% rename from artifacts/design/crash-recovery-final-768.jpg rename to evidence/design/crash-recovery-final-768.jpg diff --git a/artifacts/design/design-review.json b/evidence/design/design-review.json similarity index 100% rename from artifacts/design/design-review.json rename to evidence/design/design-review.json diff --git a/artifacts/design/landing-audit/candidate-1440.png b/evidence/design/landing-audit/candidate-1440.png similarity index 100% rename from artifacts/design/landing-audit/candidate-1440.png rename to evidence/design/landing-audit/candidate-1440.png diff --git a/artifacts/design/landing-audit/candidate-390.png b/evidence/design/landing-audit/candidate-390.png similarity index 100% rename from artifacts/design/landing-audit/candidate-390.png rename to evidence/design/landing-audit/candidate-390.png diff --git a/artifacts/design/landing-audit/candidate-768.png b/evidence/design/landing-audit/candidate-768.png similarity index 100% rename from artifacts/design/landing-audit/candidate-768.png rename to evidence/design/landing-audit/candidate-768.png diff --git a/artifacts/design/landing-audit/final-1440.png b/evidence/design/landing-audit/final-1440.png similarity index 100% rename from artifacts/design/landing-audit/final-1440.png rename to evidence/design/landing-audit/final-1440.png diff --git a/artifacts/design/landing-audit/final-390.png b/evidence/design/landing-audit/final-390.png similarity index 100% rename from artifacts/design/landing-audit/final-390.png rename to evidence/design/landing-audit/final-390.png diff --git a/artifacts/design/landing-audit/final-768.png b/evidence/design/landing-audit/final-768.png similarity index 100% rename from artifacts/design/landing-audit/final-768.png rename to evidence/design/landing-audit/final-768.png diff --git a/artifacts/design/landing-audit/final-p1-1440.png b/evidence/design/landing-audit/final-p1-1440.png similarity index 100% rename from artifacts/design/landing-audit/final-p1-1440.png rename to evidence/design/landing-audit/final-p1-1440.png diff --git a/artifacts/design/landing-audit/final-p1-390.png b/evidence/design/landing-audit/final-p1-390.png similarity index 100% rename from artifacts/design/landing-audit/final-p1-390.png rename to evidence/design/landing-audit/final-p1-390.png diff --git a/artifacts/design/landing-audit/final-p1-768.png b/evidence/design/landing-audit/final-p1-768.png similarity index 100% rename from artifacts/design/landing-audit/final-p1-768.png rename to evidence/design/landing-audit/final-p1-768.png diff --git a/.fleet/evidence/landing-audit/performance.html b/evidence/design/landing-audit/performance.html similarity index 100% rename from .fleet/evidence/landing-audit/performance.html rename to evidence/design/landing-audit/performance.html diff --git a/artifacts/design/landing-audit/rendered-1440.png b/evidence/design/landing-audit/rendered-1440.png similarity index 100% rename from artifacts/design/landing-audit/rendered-1440.png rename to evidence/design/landing-audit/rendered-1440.png diff --git a/artifacts/design/landing-audit/rendered-390.png b/evidence/design/landing-audit/rendered-390.png similarity index 100% rename from artifacts/design/landing-audit/rendered-390.png rename to evidence/design/landing-audit/rendered-390.png diff --git a/artifacts/design/landing-audit/rendered-768.png b/evidence/design/landing-audit/rendered-768.png similarity index 100% rename from artifacts/design/landing-audit/rendered-768.png rename to evidence/design/landing-audit/rendered-768.png diff --git a/.fleet/evidence/landing-audit/scorecard.json b/evidence/design/landing-audit/scorecard.json similarity index 100% rename from .fleet/evidence/landing-audit/scorecard.json rename to evidence/design/landing-audit/scorecard.json diff --git a/artifacts/design/landing-audit/scrolled-390.png b/evidence/design/landing-audit/scrolled-390.png similarity index 100% rename from artifacts/design/landing-audit/scrolled-390.png rename to evidence/design/landing-audit/scrolled-390.png diff --git a/artifacts/design/probes/review-overhaul-directions.html b/evidence/design/probes/review-overhaul-directions.html similarity index 100% rename from artifacts/design/probes/review-overhaul-directions.html rename to evidence/design/probes/review-overhaul-directions.html diff --git a/artifacts/design/probes/review-overhaul-evidence-brief.png b/evidence/design/probes/review-overhaul-evidence-brief.png similarity index 100% rename from artifacts/design/probes/review-overhaul-evidence-brief.png rename to evidence/design/probes/review-overhaul-evidence-brief.png diff --git a/artifacts/design/probes/review-overhaul-inline-review.png b/evidence/design/probes/review-overhaul-inline-review.png similarity index 100% rename from artifacts/design/probes/review-overhaul-inline-review.png rename to evidence/design/probes/review-overhaul-inline-review.png diff --git a/artifacts/design/probes/review-overhaul-triage-desk.png b/evidence/design/probes/review-overhaul-triage-desk.png similarity index 100% rename from artifacts/design/probes/review-overhaul-triage-desk.png rename to evidence/design/probes/review-overhaul-triage-desk.png diff --git a/artifacts/design/product-surfaces-after-1440.jpg b/evidence/design/product-surfaces-after-1440.jpg similarity index 100% rename from artifacts/design/product-surfaces-after-1440.jpg rename to evidence/design/product-surfaces-after-1440.jpg diff --git a/artifacts/design/product-surfaces-after-390.jpg b/evidence/design/product-surfaces-after-390.jpg similarity index 100% rename from artifacts/design/product-surfaces-after-390.jpg rename to evidence/design/product-surfaces-after-390.jpg diff --git a/artifacts/design/product-surfaces-after-768.jpg b/evidence/design/product-surfaces-after-768.jpg similarity index 100% rename from artifacts/design/product-surfaces-after-768.jpg rename to evidence/design/product-surfaces-after-768.jpg diff --git a/artifacts/design/product-surfaces-before-1440.jpg b/evidence/design/product-surfaces-before-1440.jpg similarity index 100% rename from artifacts/design/product-surfaces-before-1440.jpg rename to evidence/design/product-surfaces-before-1440.jpg diff --git a/apps/desktop/artifacts/design/product-surfaces-scope-1440.png b/evidence/design/product-surfaces-scope-1440.png similarity index 100% rename from apps/desktop/artifacts/design/product-surfaces-scope-1440.png rename to evidence/design/product-surfaces-scope-1440.png diff --git a/apps/desktop/artifacts/design/product-surfaces-scope-390.png b/evidence/design/product-surfaces-scope-390.png similarity index 100% rename from apps/desktop/artifacts/design/product-surfaces-scope-390.png rename to evidence/design/product-surfaces-scope-390.png diff --git a/apps/desktop/artifacts/design/product-surfaces-scope-768.png b/evidence/design/product-surfaces-scope-768.png similarity index 100% rename from apps/desktop/artifacts/design/product-surfaces-scope-768.png rename to evidence/design/product-surfaces-scope-768.png diff --git a/artifacts/design/review-agent-readiness-after-1440.png b/evidence/design/review-agent-readiness-after-1440.png similarity index 100% rename from artifacts/design/review-agent-readiness-after-1440.png rename to evidence/design/review-agent-readiness-after-1440.png diff --git a/artifacts/design/review-agent-readiness-after-390.png b/evidence/design/review-agent-readiness-after-390.png similarity index 100% rename from artifacts/design/review-agent-readiness-after-390.png rename to evidence/design/review-agent-readiness-after-390.png diff --git a/artifacts/design/review-agent-readiness-after-768.png b/evidence/design/review-agent-readiness-after-768.png similarity index 100% rename from artifacts/design/review-agent-readiness-after-768.png rename to evidence/design/review-agent-readiness-after-768.png diff --git a/artifacts/design/review-desktop-after-1440.png b/evidence/design/review-desktop-after-1440.png similarity index 100% rename from artifacts/design/review-desktop-after-1440.png rename to evidence/design/review-desktop-after-1440.png diff --git a/artifacts/design/review-desktop-final-1440.png b/evidence/design/review-desktop-final-1440.png similarity index 100% rename from artifacts/design/review-desktop-final-1440.png rename to evidence/design/review-desktop-final-1440.png diff --git a/artifacts/design/review-inline-overhaul-after-1280.png b/evidence/design/review-inline-overhaul-after-1280.png similarity index 100% rename from artifacts/design/review-inline-overhaul-after-1280.png rename to evidence/design/review-inline-overhaul-after-1280.png diff --git a/artifacts/design/review-inline-overhaul-after-1440.png b/evidence/design/review-inline-overhaul-after-1440.png similarity index 100% rename from artifacts/design/review-inline-overhaul-after-1440.png rename to evidence/design/review-inline-overhaul-after-1440.png diff --git a/artifacts/design/review-inline-overhaul-after-900.png b/evidence/design/review-inline-overhaul-after-900.png similarity index 100% rename from artifacts/design/review-inline-overhaul-after-900.png rename to evidence/design/review-inline-overhaul-after-900.png diff --git a/artifacts/design/review-inline-overhaul-audit-390.png b/evidence/design/review-inline-overhaul-audit-390.png similarity index 100% rename from artifacts/design/review-inline-overhaul-audit-390.png rename to evidence/design/review-inline-overhaul-audit-390.png diff --git a/artifacts/design/review-inline-overhaul-audit-768.png b/evidence/design/review-inline-overhaul-audit-768.png similarity index 100% rename from artifacts/design/review-inline-overhaul-audit-768.png rename to evidence/design/review-inline-overhaul-audit-768.png diff --git a/artifacts/design/review-inspector-readable-after-1440.png b/evidence/design/review-inspector-readable-after-1440.png similarity index 100% rename from artifacts/design/review-inspector-readable-after-1440.png rename to evidence/design/review-inspector-readable-after-1440.png diff --git a/artifacts/design/review-navigation-after-1440.png b/evidence/design/review-navigation-after-1440.png similarity index 100% rename from artifacts/design/review-navigation-after-1440.png rename to evidence/design/review-navigation-after-1440.png diff --git a/artifacts/design/review-result-polish-after-1440.png b/evidence/design/review-result-polish-after-1440.png similarity index 100% rename from artifacts/design/review-result-polish-after-1440.png rename to evidence/design/review-result-polish-after-1440.png diff --git a/artifacts/design/review-result-polish-after-390.png b/evidence/design/review-result-polish-after-390.png similarity index 100% rename from artifacts/design/review-result-polish-after-390.png rename to evidence/design/review-result-polish-after-390.png diff --git a/artifacts/design/review-result-polish-after-768.png b/evidence/design/review-result-polish-after-768.png similarity index 100% rename from artifacts/design/review-result-polish-after-768.png rename to evidence/design/review-result-polish-after-768.png diff --git a/.fleet/design-reviews/crash-recovery.json b/evidence/design/reviews/crash-recovery.json similarity index 100% rename from .fleet/design-reviews/crash-recovery.json rename to evidence/design/reviews/crash-recovery.json diff --git a/.fleet/design-reviews/desktop-navigation-clarity.json b/evidence/design/reviews/desktop-navigation-clarity.json similarity index 100% rename from .fleet/design-reviews/desktop-navigation-clarity.json rename to evidence/design/reviews/desktop-navigation-clarity.json diff --git a/.fleet/design-reviews/focus-desktop-product-surfaces.json b/evidence/design/reviews/focus-desktop-product-surfaces.json similarity index 100% rename from .fleet/design-reviews/focus-desktop-product-surfaces.json rename to evidence/design/reviews/focus-desktop-product-surfaces.json diff --git a/.fleet/design-reviews/landing-page.json b/evidence/design/reviews/landing-page.json similarity index 100% rename from .fleet/design-reviews/landing-page.json rename to evidence/design/reviews/landing-page.json diff --git a/.fleet/design-reviews/local-review-test-performance-check.json b/evidence/design/reviews/local-review-test-performance-check.json similarity index 100% rename from .fleet/design-reviews/local-review-test-performance-check.json rename to evidence/design/reviews/local-review-test-performance-check.json diff --git a/.fleet/design-reviews/review-inspector-readability.json b/evidence/design/reviews/review-inspector-readability.json similarity index 100% rename from .fleet/design-reviews/review-inspector-readability.json rename to evidence/design/reviews/review-inspector-readability.json diff --git a/.fleet/design-reviews/review-result-polish.json b/evidence/design/reviews/review-result-polish.json similarity index 100% rename from .fleet/design-reviews/review-result-polish.json rename to evidence/design/reviews/review-result-polish.json diff --git a/.fleet/design-review.json b/evidence/design/reviews/review-workbench-overhaul.json similarity index 100% rename from .fleet/design-review.json rename to evidence/design/reviews/review-workbench-overhaul.json diff --git a/.fleet/design-reviews/sidebar.json b/evidence/design/reviews/sidebar.json similarity index 100% rename from .fleet/design-reviews/sidebar.json rename to evidence/design/reviews/sidebar.json diff --git a/.fleet/design-reviews/trex-change-and-preview-verification.json b/evidence/design/reviews/trex-change-and-preview-verification.json similarity index 100% rename from .fleet/design-reviews/trex-change-and-preview-verification.json rename to evidence/design/reviews/trex-change-and-preview-verification.json diff --git a/artifacts/design/sidebar-after-1440.png b/evidence/design/sidebar-after-1440.png similarity index 100% rename from artifacts/design/sidebar-after-1440.png rename to evidence/design/sidebar-after-1440.png diff --git a/artifacts/design/sidebar-after-390.png b/evidence/design/sidebar-after-390.png similarity index 100% rename from artifacts/design/sidebar-after-390.png rename to evidence/design/sidebar-after-390.png diff --git a/artifacts/design/sidebar-after-768.png b/evidence/design/sidebar-after-768.png similarity index 100% rename from artifacts/design/sidebar-after-768.png rename to evidence/design/sidebar-after-768.png diff --git a/artifacts/design/structural-context-evaluation.html b/evidence/design/structural-context-evaluation.html similarity index 100% rename from artifacts/design/structural-context-evaluation.html rename to evidence/design/structural-context-evaluation.html diff --git a/artifacts/design/trex-after-1440.png b/evidence/design/trex-after-1440.png similarity index 100% rename from artifacts/design/trex-after-1440.png rename to evidence/design/trex-after-1440.png diff --git a/artifacts/design/trex-after-390.png b/evidence/design/trex-after-390.png similarity index 100% rename from artifacts/design/trex-after-390.png rename to evidence/design/trex-after-390.png diff --git a/artifacts/design/trex-after-768.png b/evidence/design/trex-after-768.png similarity index 100% rename from artifacts/design/trex-after-768.png rename to evidence/design/trex-after-768.png diff --git a/artifacts/design/trex-before-1440.png b/evidence/design/trex-before-1440.png similarity index 100% rename from artifacts/design/trex-before-1440.png rename to evidence/design/trex-before-1440.png diff --git a/artifacts/design/trex-result-1440.png b/evidence/design/trex-result-1440.png similarity index 100% rename from artifacts/design/trex-result-1440.png rename to evidence/design/trex-result-1440.png diff --git a/artifacts/design/usage-telemetry-after-1440.png b/evidence/design/usage-telemetry-after-1440.png similarity index 100% rename from artifacts/design/usage-telemetry-after-1440.png rename to evidence/design/usage-telemetry-after-1440.png diff --git a/artifacts/design/usage-telemetry-after-390.png b/evidence/design/usage-telemetry-after-390.png similarity index 100% rename from artifacts/design/usage-telemetry-after-390.png rename to evidence/design/usage-telemetry-after-390.png diff --git a/artifacts/design/usage-telemetry-after-768.png b/evidence/design/usage-telemetry-after-768.png similarity index 100% rename from artifacts/design/usage-telemetry-after-768.png rename to evidence/design/usage-telemetry-after-768.png diff --git a/artifacts/design/usage-telemetry-before-1440.png b/evidence/design/usage-telemetry-before-1440.png similarity index 100% rename from artifacts/design/usage-telemetry-before-1440.png rename to evidence/design/usage-telemetry-before-1440.png diff --git a/artifacts/performance/calorie-exercise-guidance-2026-08-10.md b/evidence/performance/calorie-exercise-guidance-2026-08-10.md similarity index 100% rename from artifacts/performance/calorie-exercise-guidance-2026-08-10.md rename to evidence/performance/calorie-exercise-guidance-2026-08-10.md diff --git a/artifacts/performance/old-local-portfolio-manifest.json b/evidence/performance/old-local-portfolio-manifest.json similarity index 100% rename from artifacts/performance/old-local-portfolio-manifest.json rename to evidence/performance/old-local-portfolio-manifest.json diff --git a/artifacts/performance/old-local-projects-results-2026-08-09.md b/evidence/performance/old-local-projects-results-2026-08-09.md similarity index 100% rename from artifacts/performance/old-local-projects-results-2026-08-09.md rename to evidence/performance/old-local-projects-results-2026-08-09.md diff --git a/artifacts/reviews/pr157-codevetter-review.png b/evidence/reviews/pr157-codevetter-review.png similarity index 100% rename from artifacts/reviews/pr157-codevetter-review.png rename to evidence/reviews/pr157-codevetter-review.png diff --git a/artifacts/reviews/pr173-codevetter-review.png b/evidence/reviews/pr173-codevetter-review.png similarity index 100% rename from artifacts/reviews/pr173-codevetter-review.png rename to evidence/reviews/pr173-codevetter-review.png From e960e643f68201dad64edb0cbd6f23b3a22095ff Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 13:41:06 +0530 Subject: [PATCH 05/37] docs: correct repo-structure and Blume paths to match reality The documented structure had drifted from the tree: - agents.md's "Repo structure" block omitted benchmarks/, artifacts/, docs-site/ and research/, and listed benchmark/ which has moved. - README.md's "Monorepo Structure" listed openspec/, which does not exist. - Both agents.md and docs/development/docs.md placed blume.config.ts at the repo root. It actually lives at docs-site/blume.config.ts. Also state the artifacts/ vs evidence/ split so the convention is discoverable rather than folklore. Verified with node scripts/check-docs.mjs (77 files, no broken links). Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 6 ++++-- agents.md | 13 +++++++++---- docs/development/docs.md | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3c61ff6d..79ff7365 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,10 @@ apps/ desktop/ Tauri 2 + React 19 + Vite desktop app — the core product landing-page-astro/ Astro marketing site (static export, deployed to Cloudflare Pages — codevetter.com) docs/ Canonical knowledge system — see docs/index.md -benchmark/ Public catch-rate benchmark cases + harness -openspec/ Spec-driven workflow (specs + changes/archive) +docs-site/ Blume presentation layer for docs/ (generated output is gitignored) +benchmarks/ Evaluation corpora (public catch-rate, agent PRs, runtime challenges) +evidence/ Committed run evidence (design, performance, reviews) +scripts/ Benchmark + corpus + deploy + doc-validation scripts ``` > The legacy Next.js `apps/landing-page/` was removed on 2026-07-03. The diff --git a/agents.md b/agents.md index cd1bb055..ec65f709 100644 --- a/agents.md +++ b/agents.md @@ -56,14 +56,19 @@ apps/ tests/ # Playwright e2e tests landing-page-astro/ # Astro marketing site → Cloudflare Pages (codevetter.com) docs/ # Canonical knowledge system — see docs/index.md -benchmark/ # Public catch-rate benchmark cases + harness -scripts/ # Benchmark + deploy + doc-validation scripts +docs-site/ # Blume presentation layer for docs/ (NOT the source of truth) +benchmarks/ # Evaluation corpora — public-catch-rate/, agent-prs/, + # agent-tasks/, runtime-challenges/, context-*/, performance-lab/ +evidence/ # Committed run evidence: design/, performance/, reviews/ +scripts/ # Benchmark + corpus + deploy + doc-validation scripts .github/workflows/ # ci, auto-release, release, deploy-landing, weekly, docs -blume.config.ts # Blume presentation layer for docs/ (NOT the source of truth) STATUS.md # Compatibility pointer PROJECT_STATUS.md # Current/shipped product truth (fleet source of truth) ``` +`artifacts/` is gitignored scratch for CLI runs (`--out artifacts/...`). +Committed evidence belongs in `evidence/`. + ## Key commands ```bash # From apps/desktop/ @@ -121,7 +126,7 @@ node scripts/check-docs.mjs # Validate docs (links, frontmatter, structure) The committed Markdown under `docs/` is the **source of truth** for product knowledge, architecture, decisions, workflows, operations, learnings, and -failed approaches. Blume (`blume.config.ts`) is only the presentation/search +failed approaches. Blume (`docs-site/blume.config.ts`) is only the presentation/search layer — generated output (`.blume/`) is gitignored. - **Navigation hub**: `docs/index.md` diff --git a/docs/development/docs.md b/docs/development/docs.md index 6fae096e..72081661 100644 --- a/docs/development/docs.md +++ b/docs/development/docs.md @@ -72,7 +72,7 @@ run `node_modules/.bin/blume validate`. ## Render with Blume -Blume reads `blume.config.ts` at the repo root and renders `docs/` as a +Blume reads `docs-site/blume.config.ts` and renders `docs/` as a static site. It is **not** the source of truth — it only presents the Markdown. From d9f2b3659bc5711fd94534faa218e150c55035d3 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 13:41:07 +0530 Subject: [PATCH 06/37] chore(security): catch pk_ project keys in the pre-push secret scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scan covered sk-, ghp_, gho_, AKIA, AIzaSy, xoxb- and PEM headers but not pk_, so the projectKey committed in foundry.json never tripped it. Add a pk_[A-Za-z0-9]{32,} pattern so this class of key cannot leak again. foundry.json is excluded for now, with a comment saying why: the key already in it predates the pattern, and blocking every push until it is rotated would be worse than tracking it. That exclusion should be removed once the key is rotated at the provider and moved out of the tracked file — redacting it now would not unpublish it, since it is already in committed history. Also repoints the corpus exclusion at benchmarks/public-catch-rate/. Co-Authored-By: Claude Opus 5 (1M context) --- .husky/pre-push | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index 60232096..b0c49490 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,4 +1,8 @@ # Abort push if lint fails or a known secret pattern leaks into tracked files. +# +# NOTE: foundry.json is excluded below because it already contains a committed +# projectKey (pk_...) that predates the pk_ pattern. Remove that exclusion once +# the key is rotated and moved out of the tracked file. set -e if [ -f package.json ] && grep -q '"lint"' package.json; then @@ -7,8 +11,8 @@ fi SECRETS=$(git ls-files -z 2>/dev/null \ | xargs -0 grep -lE \ - 'sk-(proj-|ant-)?[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|gho_[A-Za-z0-9]{36}|AIzaSy[A-Za-z0-9_-]{33}|xoxb-[A-Za-z0-9-]+|-----BEGIN (RSA |EC )?PRIVATE KEY-----' 2>/dev/null \ - | grep -vE '(\.example$|\.sample$|/tests?/|/__tests__/|/fixtures?/|/mocks?/|/vendor/|/\.tmp-|^benchmark/|^apps/landing-page-astro/public/benchmark/|src/commands/secret_policy\.rs$)' \ + 'sk-(proj-|ant-)?[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|gho_[A-Za-z0-9]{36}|AIzaSy[A-Za-z0-9_-]{33}|xoxb-[A-Za-z0-9-]+|pk_[A-Za-z0-9]{32,}|-----BEGIN (RSA |EC )?PRIVATE KEY-----' 2>/dev/null \ + | grep -vE '(\.example$|\.sample$|/tests?/|/__tests__/|/fixtures?/|/mocks?/|/vendor/|/\.tmp-|^benchmarks/public-catch-rate/|^apps/landing-page-astro/public/benchmark/|src/commands/secret_policy\.rs$|^foundry\.json$)' \ || true) if [ -n "$SECRETS" ]; then From 164e20ba70b4fdc14ad9cba5da0ebaf1ccefc432 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 14:45:18 +0530 Subject: [PATCH 07/37] docs(knowledge): record the third-party tooling evaluations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bank the tooling research so it survives the session that produced it. Four category pages plus a hub, all reachable from docs/index.md: - tooling-decisions.md — adopt/reject summary and the constraints behind it - tooling-secrets-and-supply-chain.md - tooling-coverage.md - tooling-agent-benchmarks.md - tooling-documentation.md — the DeepWiki reject Findings are dated 2026-08-30 and cite primary sources. Items the research could not confirm are marked UNVERIFIED rather than stated as fact — notably Devin/ACU pricing (429s on fetch), Socket pricing (403s), and whether Grype/Syft carry telemetry. Also adds a failed-approaches entry for the reason this was needed: the DeepWiki question was researched twice, in April and August 2026, reaching the same verdict both times, because the April survey sat in a top-level research/ directory that nothing linked to and check-docs never validated. That file is now in docs/knowledge/ and indexed. Co-Authored-By: Claude Opus 5 (1M context) --- docs/index.md | 13 ++ docs/knowledge/failed-approaches.md | 15 ++ docs/knowledge/tooling-agent-benchmarks.md | 124 ++++++++++++++ docs/knowledge/tooling-coverage.md | 153 ++++++++++++++++++ docs/knowledge/tooling-decisions.md | 98 +++++++++++ docs/knowledge/tooling-documentation.md | 96 +++++++++++ .../tooling-secrets-and-supply-chain.md | 146 +++++++++++++++++ 7 files changed, 645 insertions(+) create mode 100644 docs/knowledge/tooling-agent-benchmarks.md create mode 100644 docs/knowledge/tooling-coverage.md create mode 100644 docs/knowledge/tooling-decisions.md create mode 100644 docs/knowledge/tooling-documentation.md create mode 100644 docs/knowledge/tooling-secrets-and-supply-chain.md diff --git a/docs/index.md b/docs/index.md index ddcb622d..7e3f4d39 100644 --- a/docs/index.md +++ b/docs/index.md @@ -81,6 +81,19 @@ is only the presentation and search layer. - [competitive-landscape.md](./knowledge/competitive-landscape.md) — AI code review competitive landscape. - [performance-lab-publication.md](./knowledge/performance-lab-publication.md) — published optimization experiments, external PRs, and evidence boundaries. +- [tooling-decisions.md](./knowledge/tooling-decisions.md) — what CodeVetter + embeds instead of building, what it refuses, and why. Hub for the four + evaluations below. +- [tooling-secrets-and-supply-chain.md](./knowledge/tooling-secrets-and-supply-chain.md) + — secret scanners and dependency/vulnerability scanners. +- [tooling-coverage.md](./knowledge/tooling-coverage.md) — coverage collection + across Rust/TS/Python, the LCOV transport decision, diff-coverage traps. +- [tooling-agent-benchmarks.md](./knowledge/tooling-agent-benchmarks.md) — + public agent benchmark corpora, graded on determinism and licensing. +- [tooling-documentation.md](./knowledge/tooling-documentation.md) — the + DeepWiki reject and what would fit instead. +- [codebase-context-tools-landscape.md](./knowledge/codebase-context-tools-landscape.md) + — April 2026 survey of codebase indexing and context tooling. ### Learnings diff --git a/docs/knowledge/failed-approaches.md b/docs/knowledge/failed-approaches.md index 4b528312..02b3c3af 100644 --- a/docs/knowledge/failed-approaches.md +++ b/docs/knowledge/failed-approaches.md @@ -117,6 +117,21 @@ and the constraint it leaves behind. Link here when rejecting a similar idea. - **Constraint**: do not reintroduce `tauri-driver`. See [development/testing.md](../development/testing.md). +## Research parked outside `docs/` gets re-done + +- **What broke**: the DeepWiki adoption question was researched twice — once in + April 2026, again in August 2026 — reaching the same reject verdict both + times. +- **Root cause**: the April survey was committed to a top-level `research/` + directory that nothing linked to and `scripts/check-docs.mjs` never + validated. It was invisible at the moment the question was re-asked. +- **Fix**: moved to + [codebase-context-tools-landscape.md](./codebase-context-tools-landscape.md) + and linked from `docs/index.md`; `research/` removed. +- **Constraint**: evaluations and surveys go in `docs/knowledge/` with + frontmatter and an index entry. A finding that is not reachable from + `docs/index.md` will be paid for twice. + ## More lessons See also [`LESSONS.md`](https://github.com/Codevetter/codevetter/blob/main/docs/archive/LESSONS.md) for older entries diff --git a/docs/knowledge/tooling-agent-benchmarks.md b/docs/knowledge/tooling-agent-benchmarks.md new file mode 100644 index 00000000..a4ae6d88 --- /dev/null +++ b/docs/knowledge/tooling-agent-benchmarks.md @@ -0,0 +1,124 @@ +--- +title: Agent benchmark corpora +description: Evaluation of public agent/web benchmarks for licensing, deterministic grading, and offline reproducibility. +sidebar: + order: 13 +--- + +# Agent benchmark corpora + +Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for +the cross-category summary. + +CodeVetter grades with **executable evidence, not LLM opinion**. That filter +eliminates most of this field immediately: a benchmark whose scorer is a GPT +judge cannot supply ground truth for a product built on determinism. The second +filter is offline reproducibility — benchmarks driving live third-party websites +decay silently as those sites change. + +## Adopt + +### τ³-bench (tau2-bench) — best licensing and determinism profile + +**MIT** for code and data, data shipped in-repo under `data/tau2/domains/`. +1,909★, pushed 2026-08-27, very active. + +Grading is **deterministic by default**. `reward_basis` defaults to +`["DB", "COMMUNICATE"]`: the DB evaluator replays the gold actions on a *fresh* +environment to derive a target end state, then compares **by hash** — so any +trajectory producing an equivalent end state passes. `COMMUNICATE` is substring +match. The only LLM-judged evaluator, `NL_ASSERTION`, is marked experimental and +is **off by default**. + +- **No websites, no Docker.** Everything is simulated local tool APIs. +- Caveat: the **user simulator is an LLM** via LiteLLM. LiteLLM can point at a + local model, so this stays compatible with an offline deployment. +- Task counts: airline 50, retail 114, telecom 114 base (2,285 full), + banking_knowledge 97. Use the `base` split. +- Versioned grading discipline worth imitating: v1.0.1 (July 2026) fixed + `banking_knowledge` grading, and results below 1.0.1 are **not comparable** + with those at or above it. A `pre-v1.0.1` tag reproduces old behaviour. + +**τ-bench v1 is deprecated by its own authors** — its README directs users to +τ³-bench. Do not adopt v1. + +### Terminal-Bench 4.0 (via Harbor) — cleanest deterministic grader + +**Apache-2.0** for the harness and all task sets. This is the strongest +execution-backed grader in the survey: each task has `tests/test.sh` + +`tests/test_outputs.py`; `test.sh` runs pytest and writes `1` or `0` to +`/logs/verifier/reward.txt`. Verifiers run in their own container +(`environment_mode = "separate"`). **No LLM judge anywhere.** + +The topology changed substantially, and stale knowledge here is likely: + +- The repo **moved orgs** — `laude-institute/terminal-bench` now redirects to + `harbor-framework/terminal-bench-1` (legacy v1). +- **The harness is now Harbor** (`harbor-framework/harbor`, Apache-2.0, + 4,767★, v0.22.0 on 2026-08-22). +- Task sets are **separate Apache-2.0 dataset repos**: terminal-bench-2 (89 + tasks), terminal-bench-2-1 (91), and the continuous `terminal-bench` (68 live + + 90 archived, v4.0.0 on 2026-08-26). tbench.ai shows **4.0** as the active + leaderboard. +- `terminal-bench-core` naming is legacy v1. The modern registry is the Harbor + Hub, addressed as `terminal-bench/terminal-bench@latest`. + +**The one real cost: it is not offline as shipped.** The task template sets +`network_mode = "public"`, all 89 TB-2.0 tasks set `allow_internet = true`, and +the verifier itself runs `apt-get update` and curls `astral.sh`. These are +*package-registry* dependencies rather than live websites — far more tractable +than the rejected benchmarks below, but pre-baking images and mirroring +apt/PyPI is real work. + +Worth copying: oracle solutions ship with every task, and maintainers recommend +`--agent oracle -k 5` to validate a sandbox before trusting any result. + +## Viable with work + +- **TheAgentCompany** (MIT, 175 tasks) has the **strongest offline environment** + of any multi-app benchmark — a whole simulated company running locally + (GitLab, Plane, ownCloud, RocketChat) with pre-baked data and no third-party + sites. But its grader is **hybrid**: deterministic checkpoints *plus* LLM + evaluators, and evaluation requires LiteLLM credentials. Usable only if the + deterministic checkpoints are isolated. Needs 30+ GB disk. +- **Mind2Web (text)** — code MIT, dataset **CC-BY-4.0**, fully static cached + HTML traces, deterministic metrics. Genuinely offline. But it grades *action + prediction on frozen traces*, which is a different kind of evidence than + runtime behaviour. **Avoid `Multimodal-Mind2Web` — it is OpenRAIL**, a + use-restricted license, not CC-BY. +- **WebArena-Verified + BrowserGym/MiniWoB** — Apache-2.0, self-hosted Docker + sites. A deterministic subset exists if `fuzzy_match`/`ua_match` tasks are + dropped (those call an LLM) and the map site is excluded (it needs a tile and + routing backend). MiniWoB is the only trivially-offline env in BrowserGym. + Prefer ServiceNow's cleaned `webarena-verified` over vanilla WebArena. +- **OSWorld** — Apache-2.0 for code *and* data, active, 369 tasks, execution- + based metrics. Heavy: needs a VM. Use `test_nogdrive.json` (361 tasks) to + drop the Google-account dependency. + +## Reject + +| Benchmark | Reason | +|---|---| +| **WebVoyager** | GPT-4V judge; drives real Amazon/Booking/Google Flights. Last commit 2024-03-04. README admits time-sensitive tasks need manual date edits — not reproducible | +| **Mind2Web 2** | Agent-as-a-Judge, requires `OPENAI_API_KEY`, live agentic search | +| **WebCanvas / Mind2Web-Live** | Live web by design; dormant since 2025-02-06, so task validity has almost certainly decayed | +| **WorkArena** | Requires a **live cloud ServiceNow instance**; instance dataset is `gated: manual` with **no license field** on the HF card | +| **VisualWebArena** | MIT and usable, but no push since 2024-11-09 | +| **WindowsAgentArena** | Requires each user to fetch their own Windows 11 Eval ISO (90-day expiry) — cannot be redistributed | + +## Licensing landmines + +These are the items where a reasonable assumption is wrong: + +- 🚨 **Meta OpenApps — CC-BY-NC-4.0.** Commercial use prohibited. Painful, + because it is exactly the self-hosted offline app suite this product wants. +- 🚨 **TimeWarp — no LICENSE file at all.** All rights reserved by default. +- ⚠️ **Multimodal-Mind2Web is OpenRAIL**, while the text Mind2Web is CC-BY-4.0. + It does not inherit the permissive terms. +- ⚠️ **WorkArena-Instances** — HF card carries **no license field**. +- ⚠️ **AgentBench** — the repo is Apache-2.0, but three environments recompile + third-party datasets (WebShop, Mind2Web, ALFWorld) **without restating their + upstream licenses**. Repo-level Apache-2.0 does not cover the bundled `data/`. +- ℹ️ **BrowserGym and WorkArena report `NOASSERTION` via the GitHub API** — + this is a false negative caused by the Apache short-form header. Both LICENSE + files are Apache-2.0. Do not propagate the API's reading. diff --git a/docs/knowledge/tooling-coverage.md b/docs/knowledge/tooling-coverage.md new file mode 100644 index 00000000..fa3f40d1 --- /dev/null +++ b/docs/knowledge/tooling-coverage.md @@ -0,0 +1,153 @@ +--- +title: Code coverage tooling +description: Coverage collection across Rust, TypeScript and Python, the LCOV transport decision, and the two traps that produce false verdicts. +sidebar: + order: 12 +--- + +# Code coverage tooling + +Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for +the cross-category summary. + +Coverage matters to CodeVetter for one specific question: **did the agent's +change actually get exercised?** That is changed-line coverage, not project +coverage, and it shapes every decision below. + +## The stack + +Collect with per-ecosystem native tooling, normalise everything to **LCOV**, and +parse it in the Rust backend: + +| Layer | Choice | License | +|---|---|---| +| Rust collection | `cargo-llvm-cov` | MIT OR Apache-2.0 | +| JS/TS collection | per-runner native (vitest/jest v8), `c8` as universal fallback | MIT / ISC | +| Python collection | `coverage.py` | Apache-2.0 | +| Transport | LCOV + a JSON summary | (format, unlicensed) | +| Parsing | Rust `lcov` crate | MIT OR Apache-2.0 | +| Diff coverage | **compute in-backend** | — | + +LCOV is the only format every ecosystem emits, which is the entire reason to +choose it. It has **no formal specification** — the de-facto definition is the +`geninfo(1)` man page, in prose, and lcov 2.x has extended it. Pin behaviour +with your own fixtures rather than trusting a parser to match. + +## Rust — `cargo-llvm-cov` + +Dual MIT/Apache-2.0, v0.9.0 (2026-08-16). A thin wrapper over LLVM source-based +coverage: `--lcov` is literally `llvm-cov export -format=lcov`, `--json` is +`-format=text`. + +Best sidecar candidate in this whole evaluation: it publishes a +**`universal-apple-darwin`** tarball per release, releases are immutable, and +releases since 0.8.5 carry GitHub artifact attestations verifiable with +`gh attestation verify`. + +- **Offline caveat:** requires the `llvm-tools-preview` rustup component. If + absent it may try to install it. **Pre-flight check for this component before + promising an offline run.** +- `cargo llvm-cov show-env` → run any command → `cargo llvm-cov report --lcov` + is the right shape for "run the agent's own test command, then extract + evidence." +- **Do not build verdicts on Rust branch coverage.** `--branch` is unstable and + nightly-only; `--doctests` is listed under known limitations. Both are + explicitly flagged upstream. +- `cargo llvm-cov nextest` is first-class, but **nextest does not support + doctests** — run those separately and merge. + +**`cargo-tarpaulin` is a viable cross-check, and the "Linux x86_64 only" belief +about it is out of date.** Its LLVM engine is the default on macOS and Windows, +and 0.37.2 ships `aarch64-apple-darwin` and `universal-apple-darwin` binaries. +The ptrace restriction is Linux-only. Position it as secondary — `cargo-llvm-cov` +is closer to the LLVM metal with roughly 2.5× the download volume. + +## JavaScript / TypeScript + +`c8` (ISC) is **not stale** — v12.0.0 shipped 2026-07-14. Its value here is that +it **wraps any command** (`c8 npm test`, `c8 node --test`, `c8 vitest`) with no +instrumentation and no runner integration, which is exactly right for a harness +that must run someone else's test command unmodified. + +Node's built-in `--experimental-test-coverage` is the zero-dependency baseline. +Note it is still `Stability: 1 – Experimental` in Node 22, 24 and 26 despite the +test runner itself being stable. There is **no `--test-coverage-lcov` flag** — +LCOV comes from the reporter: `--test-reporter=lcov +--test-reporter-destination=lcov.info`. That reporter emits no test results, so +pair it with a second reporter. `NODE_V8_COVERAGE` auto-propagates to +`child_process.spawn` subprocesses, which is genuinely useful when the agent's +test command shells out. + +On the "istanbul is dead" claim — half wrong, and worth stating precisely. +`nyc` shipped a v18.0.0 major on 2026-02-22. The **`istanbuljs` monorepo +underneath it** is what stalled: last commit 2025-08-18, 201 open issues, and +Vitest found it necessary to maintain its own fork. Either way, avoid `nyc` as a +primary path — it requires injecting a Babel plugin into someone else's build, +which is precisely the invasive change a verification harness should not make. + +Vitest's v8 provider has done AST-aware remapping since v3.2.0 and its docs +claim parity with Istanbul, which removes the historical reason to pay the +instrumentation tax. + +## Python — `coverage.py` + +Apache-2.0, v7.16.0. **The repo moved to +`github.com/coveragepy/coveragepy`.** Prefer driving `coverage run -m pytest` +directly over `pytest-cov` — one less layer, identical output, and it works for +non-pytest suites. + +Relevant if runtime cost is part of your evidence: on **Python 3.14+ the +`sysmon` (PEP 669) core is the default** and dramatically cheaper; 3.10–3.13 use +the C tracer. The same repo will show very different overhead across Python +versions, so **record the active core** in the bundle. + +Note `--fail-under` exits with status **2**, not 1. + +## Rejected — hosted services + +**Codecov and Coveralls both upload coverage data off-machine.** For a +local-first product they are architecturally disqualified, not merely +inconvenient. Their uploader binaries are permissively licensed, so they *could* +be vendored — but every one exists solely to make that network call. There is +nothing to salvage. + +Codecov specifically: their security page says they do not store source code, +but archived raw uploads may contain it, and the Impact Analysis path sends +per-line execution counts with file paths. Their self-hosted option is a trap — +the commercial on-prem offering is end-of-lifed and bare-metal/HA deployments +are no longer supported. Note also the 2021 Bash Uploader compromise: a coverage +uploader was the supply-chain vector. Directly on point for why a verification +tool should not phone home. + +**Do not bundle `lcov` / `genhtml` / `geninfo`** — the Perl toolchain is +GPL-2.0. The format carries no license; the Rust `lcov` crate reads and writes +it under MIT/Apache-2.0. + +## Diff coverage — own this + +`diff-cover` (Apache-2.0, active) is the best external option and reads LCOV +directly. **Use it as a cross-check oracle in the test suite, not as a runtime +dependency** — it is Python, and this metric is core product logic. + +The computation is small and fully deterministic: + +``` +git diff -U0 .. → {file: set} +lcov crate parse(lcov.info) → {file: {line_no: hit_count}} +intersect → covered_changed / total_changed + + the exact uncovered line list +``` + +## Two traps that produce false verdicts + +1. **V8 coverage only reports files that were *loaded*.** A source file the + tests never import is simply **absent**, which is indistinguishable from 0%. + An agent that adds a brand-new untested module would therefore score + *better*. Force never-loaded files into the report: `--all --src` (c8), + `--test-coverage-include-all` (Node), `coverage.all` (vitest), + `collectCoverageFrom` (jest). +2. **Line-granular LCOV under-reports on dense lines.** For "did this exact + changed expression run?", `DA:` records lose sub-line detail. Store the + high-precision artifact alongside LCOV where available — + `cargo-llvm-cov --json` is LLVM region-level; monocart's `v8-json` preserves + byte ranges. diff --git a/docs/knowledge/tooling-decisions.md b/docs/knowledge/tooling-decisions.md new file mode 100644 index 00000000..b717a940 --- /dev/null +++ b/docs/knowledge/tooling-decisions.md @@ -0,0 +1,98 @@ +--- +title: Third-party tooling decisions +description: What CodeVetter embeds instead of building, what it refuses, and why — the hub page for per-category evaluations. +sidebar: + order: 10 +--- + +# Third-party tooling decisions + +CodeVetter should embed proven tools rather than reimplement them, but the +product's positioning narrows the field hard. Two constraints disqualify most +of the market before features are even considered: + +1. **Privacy is the product.** CodeVetter verifies code on the user's machine. + Any tool that transmits source, manifests, or lockfiles to a third party is + rejected regardless of quality. +2. **Local-first and offline.** The desktop app works without a network. Tools + requiring a hosted engine, an account, or a live API are rejected. + +A third constraint governs licensing: this is a commercial desktop product that +**redistributes** what it bundles. AGPL, SSPL, and non-commercial licenses are +blockers. MPL and GPL are subprocess-only at best. + +Research verified **2026-08-30**. Licensing, pricing, and maintenance facts +decay — re-check before acting on anything here. Items the research could not +confirm are marked UNVERIFIED on the detail pages and should be treated as open +questions, not findings. + +## Adopt + +| Tool | License | Surface | Category detail | +|---|---|---|---| +| `cargo-audit` (via `rustsec` crate) | MIT OR Apache-2.0 | In-process in `src-tauri/` | [supply chain](./tooling-secrets-and-supply-chain.md) | +| `gitleaks` | MIT | Sidecar binary | [supply chain](./tooling-secrets-and-supply-chain.md) | +| `cargo-llvm-cov` | MIT OR Apache-2.0 | Sidecar binary | [coverage](./tooling-coverage.md) | +| `lcov` crate | MIT OR Apache-2.0 | In-process parser | [coverage](./tooling-coverage.md) | +| τ³-bench (tau2-bench) | MIT | Corpus under `benchmarks/` | [agent benchmarks](./tooling-agent-benchmarks.md) | +| Terminal-Bench 4.0 (Harbor) | Apache-2.0 | Corpus under `benchmarks/` | [agent benchmarks](./tooling-agent-benchmarks.md) | + +`cargo-audit` is the highest-leverage item: the `rustsec` crate runs inside the +existing Rust backend with no new process boundary, no sidecar to codesign, and +no subprocess. Its SARIF 2.1.0 output is real but undocumented in the README and +absent from the changelog — pin `>= 0.22.0` and trust the source, not the docs. + +## Reject + +| Tool | Reason | +|---|---| +| TruffleHog | AGPL-3.0, **and** verifies secrets against live provider APIs by default | +| ggshield | Hosted detection engine; file content necessarily transmitted | +| socket.dev | Hosted; uploads manifests and lockfiles | +| Codecov / Coveralls | Exist to upload coverage data off-machine | +| npm / pnpm audit | No offline mode; npm's fallback path uploads the full dependency tree plus machine metadata | +| `lcov` / `genhtml` Perl tooling | GPL-2.0 — do not bundle (the *format* is unencumbered) | +| Meta OpenApps | CC-BY-NC-4.0, commercial use prohibited | +| DeepWiki | Hosted; private repos need a paid Devin account — see [documentation tooling](./tooling-documentation.md) | +| WorkArena, WebVoyager | Require live third-party websites or hosted SaaS | +| detect-secrets | Dormant since 2024; no SARIF | +| Nosey Parker | Archived 2026-04-24, superseded by Titus | + +## Adopt only with explicit configuration + +- **Trivy** ships telemetry **on by default**, contacting `check.trivy.dev`. + Requires `--skip-version-check --disable-telemetry`. Separately, + `--offline-scan` does *not* mean offline — it only suppresses + dependency-identification API calls, not DB downloads or telemetry. +- **Terminal-Bench** needs network access **at verification time**. Its task + template sets `network_mode = "public"`, all 89 TB-2.0 tasks set + `allow_internet = true`, and the verifier's own `test.sh` runs `apt-get + update` and curls `astral.sh`. These are package-registry dependencies rather + than live websites, so pre-baking images is tractable — but it is real work, + not a flag. +- **cdxgen v13** requires `pnpm >= 11` and `node >= 24`, which conflicts with + this repo's `pnpm@10.33.2`. Pin `@cyclonedx/cdxgen@12.x`, or prefer Syft. + +## What not to outsource + +Changed-line coverage — the metric that answers "did the agent's change +actually get exercised?" — should be computed in the Rust backend, not +delegated. It is a small deterministic join: `git diff -U0` yields changed +lines, LCOV yields hit counts, intersect them. It is the core verdict input, it +must be reproducible and explainable inside the evidence bundle, and it must not +depend on a Python install existing on the user's machine. Use `diff-cover` +(Apache-2.0) as a cross-check oracle in the test suite instead of a runtime +dependency. + +Two traps that produce false verdicts if ignored are documented in +[coverage](./tooling-coverage.md): V8's loaded-files-only blind spot, and +LCOV's line-granularity loss on dense lines. + +## Related + +- [codebase-context-tools-landscape.md](./codebase-context-tools-landscape.md) + — April 2026 survey of codebase indexing and context tooling. Its DeepWiki + assessment was independently reconfirmed in August 2026. +- [failed-approaches.md](./failed-approaches.md) — constraints left behind by + things that broke. Check before adopting anything that touches the package + manager or the data layer. diff --git a/docs/knowledge/tooling-documentation.md b/docs/knowledge/tooling-documentation.md new file mode 100644 index 00000000..7e014b72 --- /dev/null +++ b/docs/knowledge/tooling-documentation.md @@ -0,0 +1,96 @@ +--- +title: Documentation tooling — the DeepWiki decision +description: Why DeepWiki is not adopted for CodeVetter's documentation, and what would fit if auto-generated architecture docs are wanted. +sidebar: + order: 14 +--- + +# Documentation tooling — the DeepWiki decision + +Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for +the cross-category summary. + +**Verdict: reject** DeepWiki as a documentation pipeline dependency. The only +form that composes with the existing stack is a passive outbound link (a README +or landing badge to `deepwiki.com/Codevetter/codevetter`), not an integration. + +## Why + +### The privacy question is subtler than it first appears + +CodeVetter's own repository is **public**, so the free hosted deepwiki.com and +its MCP server (`https://mcp.deepwiki.com/mcp`) already work on it today with +zero setup. The private-repo blocker does not bite *this* repo. + +It bites hard the moment DeepWiki-style indexing touches the product's actual +privacy-sensitive surface: **the user repositories being reviewed**. Adopting +the pattern for our own docs normalises a dependency that cannot be extended to +the product itself without contradicting its central promise. + +### Private repos are paid, and nothing runs offline + +Private repositories require a **paid Devin account** (ACU-billed). The free +public MCP server and web UI work only on public repos. + +**Exact pricing is UNVERIFIED** — both `devin.ai/pricing` and +`cognition.com/pricing` returned HTTP 429 to every fetch attempt. + +Nothing about the deepwiki.com product runs offline. The only offline-capable +option is a **different, independent project** (`AsyncFuncAI/deepwiki-open`), +not the hosted product. + +### It fails on process grounds independently of privacy + +Even setting privacy aside, DeepWiki conflicts with this repo's own documented +rules: + +- **"Markdown under `docs/` is the source of truth."** A hosted generated wiki + is a second, non-authoritative home for the same facts. +- **"One canonical home per fact."** It duplicates by construction. +- It is invisible to `scripts/check-docs.mjs` and to Blume's navigation, so + nothing validates its links or keeps it in the sidebar. + +## This was already known + +[`codebase-context-tools-landscape.md`](./codebase-context-tools-landscape.md) +assessed DeepWiki in **April 2026** and reached the same conclusion. That +document sat in a top-level `research/` directory that nothing linked to, so the +finding was effectively lost and the question got asked again. + +The August 2026 re-check confirmed the April verdict still holds, and that the +three self-hosted alternatives it named remain active: + +| Project | Stars | License | Last push | +|---|---|---|---| +| `AsyncFuncAI/deepwiki-open` | 17.8k | MIT | 2026-08-16 | +| `AIDotNet/OpenDeepWiki` | 3.5k | MIT | 2026-08-27 | +| `sopaco/deepwiki-rs` | 1.7k | MIT | 2026-08-14 | + +The reusable lesson is filed in +[failed-approaches.md](./failed-approaches.md): research that lives outside +`docs/` gets re-done. + +## If auto-generated architecture docs are still wanted + +**`sopaco/deepwiki-rs`** (MIT) is the closest fit. Use it as a **generator, not +a service**: + +1. Run it locally to produce Mermaid/C4 markdown. +2. Commit the output into `docs/architecture/`. +3. Let it go through normal PR review like any other change. + +That keeps markdown as the source of truth, keeps `check-docs.mjs` and Blume in +control of validation and navigation, and adds no runtime dependency or network +call. **Never embed it as a live service** — that reintroduces every problem +listed above. + +`AsyncFuncAI/deepwiki-open` ships Ollama Docker configs and is the option to +look at if fully local model-driven generation is ever wanted, at the cost of +running a container stack. + +## Method note + +WebSearch was already at its session budget cap when this was verified, so all +facts came from direct fetches of primary sources (deepwiki.com, docs.devin.ai) +and `gh api` / `gh search` against GitHub. No claims here come from model +memory. Pricing remains the one unverified item. diff --git a/docs/knowledge/tooling-secrets-and-supply-chain.md b/docs/knowledge/tooling-secrets-and-supply-chain.md new file mode 100644 index 00000000..ebfbb8c4 --- /dev/null +++ b/docs/knowledge/tooling-secrets-and-supply-chain.md @@ -0,0 +1,146 @@ +--- +title: Secret scanning and supply-chain tooling +description: Evaluation of secret scanners and dependency/vulnerability scanners against CodeVetter's offline and privacy constraints. +sidebar: + order: 11 +--- + +# Secret scanning and supply-chain tooling + +Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for +the cross-category summary and the constraints that drive these calls. + +The headline: a common assumption going in was that several core scanners had +relicensed to something restrictive. Checked against LICENSE files at HEAD, that +is **wrong** — Trivy, Grype, Syft, osv-scanner, cdxgen, Dependency-Track, +detect-secrets, Titus, and Kingfisher are all Apache-2.0, and Gitleaks is still +MIT. TruffleHog is AGPL-3.0, but has been since v3.0 in 2021. + +What actually matters is different and less obvious: **default network +behaviour**. Several tools phone home or validate credentials against live +provider APIs unless told not to. + +## Recommended + +### `cargo-audit` — embed in the Rust backend + +Dual **MIT OR Apache-2.0**. The highest-leverage item in this category because +the underlying `rustsec` crate is a **library**: it runs in-process in +`src-tauri/` with no sidecar binary to codesign and notarize, and no subprocess. + +- **SARIF 2.1.0 output exists but is undocumented.** It is absent from the + README and the CHANGELOG stops before it landed (~Sept 2025). Verified in + `cargo-audit/src/sarif.rs`. Pin `>= 0.22.0`. +- Offline: `--no-fetch --stale`, or `database.fetch = false` in `audit.toml`. +- The advisory DB is a git clone of + [RustSec/advisory-db](https://github.com/RustSec/advisory-db) at + `~/.cargo/advisory-db` — trivially vendorable and shippable. +- Pair with **cargo-deny** (`--offline`) for SPDX license-policy enforcement, + which nothing else here does for Rust. Note its output is structured JSON log + lines, **not SARIF** — the mapping would be yours to write. + +### `gitleaks` — bundle as a sidecar + +**MIT**, and the core scanner's license is unchanged. Fully offline: no DB, no +network, rules embedded or from `.gitleaks.toml`. Emits SARIF. Static Go binary, +same integration shape as the existing `ccusage` sidecar. + +One licensing nuance worth stating precisely, because it is widely misreported: +the commercial relicensing applies **only to `gitleaks/gitleaks-action` v2.0.0+** +(MIT → proprietary EULA). Invoking the MIT Go binary directly is unencumbered. + +Caution: v8.30.1 shipped 2026-03-21 and recent commits are largely Dependabot. +Not stale, but feature velocity has slowed. + +## Rejected + +### TruffleHog — two independent blockers + +**AGPL-3.0** (LICENSE at `main` verified). Bundling an AGPL binary in a +distributed proprietary desktop app is a genuine legal question — subprocessing +an unmodified binary is the lowest-risk posture, but redistribution still +carries source-offer obligations. Get counsel before shipping. + +The second blocker is worse and is about defaults: "verified" in TruffleHog +means it **tested the candidate credential against the live provider API** — the +AWS detector performs a real `GetCallerIdentity` call. In practice, strings from +the user's private source are transmitted to AWS, GitHub, Slack and hundreds of +others **by default**. Mitigable with `--no-verification`, but it is opt-out, and +one missing flag is a privacy incident. + +### ggshield and socket.dev — hosted engines + +`ggshield`'s MIT license covers **the client only**; detection runs server-side +and requires a GitGuardian API key. Their docs are precisely worded — "your +files and secrets won't be stored" is a statement about *retention*, not +transmission. File content necessarily crosses the wire. No offline mode. + +socket.dev uploads manifests and lockfiles (not full source, per their docs). +Also note: `SocketDev/socket-cli` has **no LICENSE file at repo root** while the +published npm packages declare MIT — do not rely on the MIT claim. +Pricing is **UNVERIFIED** (`socket.dev/pricing` returns 403 to automated fetch). + +### npm / pnpm audit — the fallback is the hazard + +The default bulk path sends package names and versions, which is acceptable. But +npm's **Quick Audit fallback**, triggered automatically when bulk fails, submits +*"the full package tree as found in `package-lock.json`"* plus `npm_version`, +`node_version`, `platform`, `arch`, `node_env`. A complete dependency graph and +machine fingerprint, triggered by a transient failure outside your control. + +**No offline mode exists for either.** For an offline story, read the lockfile +locally with osv-scanner or grype instead. + +### Others + +- **detect-secrets** — Apache-2.0 and fully offline, but last release + 2024-05-06, 178 open issues, no SARIF, and it is Python (a runtime to bundle). +- **Nosey Parker** — **archived 2026-04-24**; README directs users to Titus. +- **Dependency-Track** — Apache-2.0 and active, but it is a **JVM API server + plus separate frontend** needing 8GB RAM and external Postgres. A non-starter + inside a Tauri app. Plausible only as an optional external SBOM sink. + +## Viable alternatives, with eyes open + +- **Grype + Syft** (Apache-2.0) have the cleanest offline story in the vuln set: + single static Go binaries, no daemon, `GRYPE_DB_AUTO_UPDATE=false` for + air-gap, and Syft needs no DB at all. Grype emits SARIF. + **UNVERIFIED:** whether either has telemetry. None was found, but their source + was not audited the way Trivy's was — confirm before making any "zero outbound + connections" claim. +- **Kingfisher** (MongoDB, Apache-2.0) is **written in Rust and exposes library + crates**, so it could scan in-process like `cargo-audit`. Uniquely, its + network validators are behind optional cargo features, meaning they can be + **compiled out entirely** — a compile-time guarantee stronger than any runtime + flag. Two caveats: validation is **on by default** (opt out via + `--no-validate`), and it is **not on crates.io** — use a git dependency; the + unrelated `kingfisher` crate on crates.io is a different package. +- **Titus** (Praetorian, Apache-2.0) is the Nosey Parker successor with the best + defaults in the category — **validation is opt-in**. Risk: created 2026-01-25, + ~7 months old. + +## Trivy — usable, but not as-is + +Apache-2.0, no relicensing, excellent format support including SARIF. Two +corrections to common belief: + +1. **Telemetry is on by default**, sending an install identifier (one-way hash + of a machine fingerprint), version, and OS to `check.trivy.dev`. No scan + results or file paths. Not a code leak — but an unsolicited outbound + connection from a privacy-positioned app is a positioning problem regardless + of payload. Disable with `--skip-version-check --disable-telemetry`. +2. **`--offline-scan` does not mean offline.** Its actual usage string is *"do + not issue API requests to identify dependencies"*. True air-gap additionally + needs `--download-db-only` once to seed, then `--skip-db-update + --skip-java-db-update`. + +Useful detail: the misconfiguration checks bundle is **embedded in the binary at +build time**, so that scanning survives with zero network. + +## SBOM formats + +Target **CycloneDX 1.7** (ECMA-424 2nd Edition, patch 1.7.1 2026-06-02) and +**SPDX 3.0.1** (3.1 is still RC). Prefer **Syft** as the generator — bigger, Go +binary, no Node floor. Reach for cdxgen only where Syft's ecosystem coverage +falls short, and pin `@cyclonedx/cdxgen@12.x`: v13 moved npm scope and requires +`node >= 24` / `pnpm >= 11`. From bf5f1b75fc1c812a0ebe053ba2f38d64dd50872f Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 14:49:04 +0530 Subject: [PATCH 08/37] docs(knowledge): record sandboxed-execution and static-analysis evaluation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the tooling research the first agent died before delivering. Adds tooling-sandboxing.md and wires it into the hub and index. Recommendation: libkrun (Apache-2.0, Rust, Hypervisor.framework) embedded in-process in src-tauri/, with apple/containerization as the upgrade path once a Swift sidecar is acceptable — it is the only option with native VM state save/restore, the actual warm-start mechanism. Three findings that overturn common assumptions, all recorded: - Firecracker and gVisor do not run on macOS at all (KVM / Linux platforms only). Any plan built on them is dead on arrival. - Docker Desktop is a licensing gate, not just an install burden — free only under 250 employees AND under $10M revenue. - CodeQL is legally disqualified for this product: its license forbids use on non-open-source codebases and automated analysis without paid GHAS, and CodeVetter exists to run against private repos. Also notes that Semgrep's engine (LGPL-2.1) and its registry rules (a separate restrictive license forbidding redistribution) are licensed differently, and that observability needs no new dependency — the existing SQLite tables cover per-run cost and latency. Open questions are marked UNVERIFIED, notably the Virtualization.framework entitlement question, which needs a direct check against Apple's docs. Co-Authored-By: Claude Opus 5 (1M context) --- docs/index.md | 4 +- docs/knowledge/tooling-decisions.md | 4 + docs/knowledge/tooling-sandboxing.md | 168 +++++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 docs/knowledge/tooling-sandboxing.md diff --git a/docs/index.md b/docs/index.md index 7e3f4d39..a4e75995 100644 --- a/docs/index.md +++ b/docs/index.md @@ -82,7 +82,7 @@ is only the presentation and search layer. - [performance-lab-publication.md](./knowledge/performance-lab-publication.md) — published optimization experiments, external PRs, and evidence boundaries. - [tooling-decisions.md](./knowledge/tooling-decisions.md) — what CodeVetter - embeds instead of building, what it refuses, and why. Hub for the four + embeds instead of building, what it refuses, and why. Hub for the five evaluations below. - [tooling-secrets-and-supply-chain.md](./knowledge/tooling-secrets-and-supply-chain.md) — secret scanners and dependency/vulnerability scanners. @@ -92,6 +92,8 @@ is only the presentation and search layer. public agent benchmark corpora, graded on determinism and licensing. - [tooling-documentation.md](./knowledge/tooling-documentation.md) — the DeepWiki reject and what would fit instead. +- [tooling-sandboxing.md](./knowledge/tooling-sandboxing.md) — isolation for + running untrusted agent code, determinism controls, SARIF analyzers. - [codebase-context-tools-landscape.md](./knowledge/codebase-context-tools-landscape.md) — April 2026 survey of codebase indexing and context tooling. diff --git a/docs/knowledge/tooling-decisions.md b/docs/knowledge/tooling-decisions.md index b717a940..61057b85 100644 --- a/docs/knowledge/tooling-decisions.md +++ b/docs/knowledge/tooling-decisions.md @@ -36,6 +36,7 @@ questions, not findings. | `lcov` crate | MIT OR Apache-2.0 | In-process parser | [coverage](./tooling-coverage.md) | | τ³-bench (tau2-bench) | MIT | Corpus under `benchmarks/` | [agent benchmarks](./tooling-agent-benchmarks.md) | | Terminal-Bench 4.0 (Harbor) | Apache-2.0 | Corpus under `benchmarks/` | [agent benchmarks](./tooling-agent-benchmarks.md) | +| `libkrun` | Apache-2.0 | In-process VMM in `src-tauri/` | [sandboxing](./tooling-sandboxing.md) | `cargo-audit` is the highest-leverage item: the `rustsec` crate runs inside the existing Rust backend with no new process boundary, no sidecar to codesign, and @@ -54,6 +55,9 @@ absent from the changelog — pin `>= 0.22.0` and trust the source, not the docs | `lcov` / `genhtml` Perl tooling | GPL-2.0 — do not bundle (the *format* is unencumbered) | | Meta OpenApps | CC-BY-NC-4.0, commercial use prohibited | | DeepWiki | Hosted; private repos need a paid Devin account — see [documentation tooling](./tooling-documentation.md) | +| CodeQL | License forbids use on non-open-source codebases without paid GHAS — see [sandboxing](./tooling-sandboxing.md) | +| Firecracker, gVisor | Linux-kernel only; no macOS host mode exists | +| Docker Desktop | Proprietary GUI app, license-gated at 250 employees / $10M revenue | | WorkArena, WebVoyager | Require live third-party websites or hosted SaaS | | detect-secrets | Dormant since 2024; no SARIF | | Nosey Parker | Archived 2026-04-24, superseded by Titus | diff --git a/docs/knowledge/tooling-sandboxing.md b/docs/knowledge/tooling-sandboxing.md new file mode 100644 index 00000000..597ce8f1 --- /dev/null +++ b/docs/knowledge/tooling-sandboxing.md @@ -0,0 +1,168 @@ +--- +title: Sandboxed execution and static analysis +description: Isolation options for running untrusted agent code on Apple Silicon, determinism controls, SARIF-emitting analyzers, and the CodeQL licensing blocker. +sidebar: + order: 15 +--- + +# Sandboxed execution and static analysis + +Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for +the cross-category summary. + +Sandboxed execution is the mechanism CodeVetter's verdicts rest on: if the +isolation is not reproducible, the evidence is not either. This page covers +isolation on Apple Silicon, the determinism controls that make runs comparable, +and static analyzers that emit SARIF. + +## Recommended: `libkrun`, with a VZ upgrade path + +**`libkrun`** (Apache-2.0, `containers/libkrun`, 2,643★) is a small VMM +**library** written in Rust and built on Apple's `Hypervisor.framework`. It is +not Docker, not a daemon, and not a subprocess — it links into the existing Rust +backend via its C API, boots a minimal Linux microVM per verification run, and +tears it down after. That matches the local-first, no-server constraint without +requiring the user to install and license a GUI application. + +**`apple/containerization`** (Apache-2.0, Swift, 8.9k★) is the follow-on once a +Swift sidecar is acceptable. It claims **sub-second** boot and is the only +option here with **native VM state save/restore** — the actual warm-start +mechanism. It requires **macOS 26** and Apple Silicon. + +**Colima + Lima** (MIT / Apache-2.0) is the pragmatic fallback for pre-macOS-26 +or Intel machines: a one-time Homebrew install rather than a bundled component. + +## Comparison + +| Option | License | Daemon? | Bundleable? | macOS ARM? | +|---|---|---|---|---| +| **libkrun** | Apache-2.0 | No — in-process C API | Yes, as a Rust dependency | Yes, explicit HVF backend | +| **apple/containerization** | Apache-2.0 | No (the `container` CLI wrapping it does) | Via a Swift sidecar | Apple Silicon, **macOS 26 only** | +| **Colima + Lima** | MIT / Apache-2.0 | CLI-managed VM, no GUI daemon | Prerequisite only | Yes (`vz` driver, default since Lima v1.0) | +| **Podman** | Apache-2.0 | `podman machine` VM | Prerequisite only | Yes | +| **Docker Desktop** | Engine Apache-2.0; **Desktop app proprietary** | Yes, GUI app + background VM | No | Yes, but license-gated | +| **Firecracker** | Apache-2.0 | — | — | **No — KVM only** | +| **gVisor** | Apache-2.0 | — | — | **No — Linux platforms only** | +| **Wasmtime / WasmEdge** | Apache-2.0 | No | Yes, trivially | Yes | + +### Three findings that overturn common assumptions + +1. **Firecracker and gVisor do not run on macOS at all.** Firecracker's own + production-host docs state it relies on KVM, which is Linux-only. gVisor's + platform docs list KVM, Systrap and Ptrace — all Linux-specific. There is no + ARM-macOS host mode for either. Any plan built around them is dead on + arrival. +2. **Docker Desktop is a licensing gate, not just an install burden.** The + engine (moby) is Apache-2.0, but the Desktop app is proprietary and metered: + free only under 250 employees **and** under $10M revenue. That becomes a + legal problem the moment the company crosses either threshold. +3. **WASM cannot be the primary sandbox.** It is fast and trivially embeddable, + but only runs workloads compiled to WASI. CodeVetter's actual core workload + is TypeScript/Node web tasks with real browser and API behaviour. Keep WASM + for pure-computation graders only. + +## Determinism controls + +These are mostly **guest/application-layer** concerns, so they transfer across +whichever isolation option is chosen: + +| Control | Mechanism | +|---|---| +| Frozen clock | `libfaketime` via `LD_PRELOAD` inside the guest | +| Seeded RNG | Runtime-level shims, not a hypervisor feature | +| Network isolation | `--network none` or equivalent on all container/VM options | +| Filesystem rollback | Copy-on-write layers (recreate per run, not a memory snapshot) | +| CPU/memory limits | cgroups-equivalent flags; WASM adds fuel/instruction limits | +| **VM state save/restore** | **VZ/HVF family only** — see below | + +**Warm start is the strongest argument for the VZ/HVF family.** True memory-state +snapshotting is confirmed only for `Virtualization.framework`-based options. +Lima's `vz: implement auto save/restore` ([PR #2900](https://github.com/lima-vm/lima/pull/2900)) +measured **37s → 13s** boot-to-ready, and notes `saveOnStop` requires macOS 14+ +and is arm64-only. Docker's and containerd's copy-on-write layer model does not +give you this. + +## Rust crates for driving isolation in-process + +- **`libkrun`** — the VMM itself, Rust, Apache-2.0, consumable via `bindgen` + FFI. The most direct no-shell-out path. +- **`bollard`** — async Docker Engine API client, Apache-2.0, v0.21.1 + (2026-08-16). Avoids shelling out to the `docker` CLI but still needs a + running daemon. +- **`testcontainers`** (Rust) — MIT/Apache-2.0, v0.28.0. Higher-level, same + daemon prerequisite. +- Direct Rust bindings to `Virtualization.framework` exist + (`virtualization-rs`, `apple-virtualization`, `virt-fwk`) but are all + low-star, thinly maintained side projects. **Do not depend on them.** + +## Static analysis with SARIF + +| Tool | License | Offline | Native SARIF | Verdict | +|---|---|---|---|---| +| **Biome** | Apache-2.0 | Yes | Present, fidelity **UNVERIFIED** | **Check first** — already the repo's linter | +| **ast-grep** | MIT | Yes | Present, fidelity **UNVERIFIED** | Rust-native, good stack fit | +| **Ruff** | MIT | Yes | Yes (`--output-format=sarif`) | Python scope only | +| **Semgrep** | CLI is LGPL-2.1 | Yes | Yes | Engine fine; **rules are the problem** | +| **Clippy** | Apache-2.0 | Yes | **No** | Needs `clippy-sarif` converter | +| **ESLint** | MIT | Yes | No | Third-party formatter | +| **CodeQL** | Custom | Yes | Yes | 🚫 **Legally disqualified** | + +### 🚫 CodeQL is a hard blocker for this product + +The CLI license prohibits use *"in connection with any codebase that is not an +Open Source Codebase"* and prohibits automated analysis, CI or CD on such code, +absent a paid GitHub Advanced Security agreement. + +CodeVetter's entire purpose is running against users' **private** repositories. +CodeQL therefore cannot be a default engine unless every user separately holds a +commercial GHAS license. This is a legal blocker, not a preference. + +### ⚠️ Semgrep — the engine and the rules have different licenses + +The CLI is LGPL-2.1 and runs offline. But the **registry rulesets** (`p/default`, +`p/security-audit`, …) are under a separate *Semgrep Rules License v1.0*: +*"You may use the rules only for your own internal business purposes. This +license does not allow you to distribute the rules, or to make them available to +others as a service."* + +Running Semgrep against a user's own code is internal use and fine. +**Bundling a vendored copy of the registry rules into every install is the gray +area** — that needs counsel before shipping, and is not cleared by the engine's +LGPL alone. + +**Lowest-friction path: verify Biome's own SARIF fidelity first.** The repo +already runs Biome for `pnpm lint`, so if its SARIF output is adequate, no +second linter is needed at all. + +## Observability — do not add a dependency + +**Use the existing SQLite database.** Cost, latency and tokens per run are a +handful of columns on the existing run/verification tables: `cost_usd`, +`latency_ms`, `tokens_in`, `tokens_out`, `provider`, `model`. + +- **OpenTelemetry Rust** (Apache-2.0) is well-maintained but built for exporting + to a collector. Running it in-process purely to write rows you could write + directly is dependency accumulation. +- **Langfuse** core is MIT and self-hostable, but it is a server product + (Postgres + web app) — a flat mismatch with "Tauri desktop binary, no server." + +Revisit OTel only if CodeVetter grows a genuine multi-process or remote-agent +topology needing distributed tracing. + +## Open questions + +Flagged UNVERIFIED and worth closing before committing engineering time: + +- **The `Virtualization.framework` entitlement question.** Apple's docs are + JS-rendered and could not be fetched. Circumstantial evidence is strong — + Lima, Colima and `apple/container` all ship as notarized, non-App-Store + binaries using `vz` without incident, supporting the common understanding that + `com.apple.security.virtualization` is required only for **sandboxed Mac App + Store** apps, not Developer-ID-signed builds. Confirm directly against Apple + before relying on it. +- **`libkrun` cold-boot time on Apple Silicon.** "Smallest possible boot time" + is a stated design goal; no published benchmark was found. +- **Biome and ast-grep SARIF fidelity.** Both have SARIF code in-repo; neither + was validated against real output. +- **Podman's default macOS backend** (applehv vs libkrun vs QEMU) and its exact + network/CPU/memory flags. From e97430900c027eb337e786224876a239d52e8fb5 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 14:57:22 +0530 Subject: [PATCH 09/37] docs: correct the nav claim in agents.md to the 6 shipped tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit agents.md still advertised "Nav (7 tabs)" including Work (/agents) and Board (/board). Both were retired on 2026-08-16 — docs/product/surfaces.md records the retirement audit — and App.tsx now redirects them to "/". The sidebar ships six tabs, and Performance (/performance) was missing from the list entirely. This is the same block corrected earlier in this branch for repo structure; the nav line three rows below it was missed on that pass. Co-Authored-By: Claude Opus 5 (1M context) --- agents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents.md b/agents.md index ec65f709..38610c44 100644 --- a/agents.md +++ b/agents.md @@ -97,7 +97,7 @@ node scripts/check-docs.mjs # Validate docs (links, frontmatter, structure) - **`isTauriAvailable()` guard**: all IPC calls wrapped so React code also works in plain browser. - **DB is `rusqlite`, not `@tauri-apps/plugin-sql`.** Do not re-add `plugin-sql` (removed in the 2026-07-11 desloppification sweep). See `docs/architecture/data-model.md`. - **Single package manager: pnpm.** Do not reintroduce `package-lock.json` — dual-lockfile drift broke Cloudflare Pages in May 2026. See `docs/knowledge/failed-approaches.md`. -- **Nav (7 tabs)**: Usage (`/`), Repo Unpack (`/unpack`), Work (`/agents`), Board (`/board`), Review (`/review`), Testing (`/trex`), Settings (`/settings`). Full surface map in `docs/product/surfaces.md`. +- **Nav (6 tabs)**: Usage (`/`), Repo Unpack (`/unpack`), Review (`/review`), Testing (`/trex`), Performance (`/performance`), Settings (`/settings`). Work (`/agents`) and Board (`/board`) were retired 2026-08-16 and now redirect. Full surface map in `docs/product/surfaces.md`. - **GH Actions**: `ci.yml` (lint + typecheck + unit + MCP + build), `auto-release.yml` → `release.yml` (Tauri binaries), `deploy-landing.yml` (Cloudflare Pages), `weekly.yml` (Mon cron canary), `docs.yml` (doc validation). See `docs/operations/`. - Husky pre-commit runs lint-staged on `apps/desktop/src/**/*.{ts,tsx}`; pre-push runs lint + secret scan. From 225480e5b6a018014bef1c190ed8ac3914a472e7 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 30 Aug 2026 16:47:45 +0530 Subject: [PATCH 10/37] refactor: remove shared AI gateway preset --- .../business_rule_archaeology/synthesis_command.rs | 1 - .../business_rule_archaeology/synthesis_runtime.rs | 10 ++-------- apps/desktop/src/lib/review-service.test.ts | 2 +- apps/desktop/src/lib/review-service.ts | 4 ---- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_command.rs b/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_command.rs index 1582eb97..db2c40c9 100644 --- a/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_command.rs +++ b/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_command.rs @@ -154,7 +154,6 @@ impl ArchaeologyProviderFactory for EnvironmentArchaeologyProviderFactory { ArchaeologyProviderKind::Local => None, ArchaeologyProviderKind::Hosted => { let variable = match descriptor.provider_identity.as_str() { - "free-ai" => "FREE_AI_API_KEY", "openai" => "OPENAI_API_KEY", "anthropic" => "ANTHROPIC_API_KEY", "openrouter" => "OPENROUTER_API_KEY", diff --git a/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_runtime.rs b/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_runtime.rs index abd92838..e3c6a97a 100644 --- a/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_runtime.rs +++ b/apps/desktop/src-tauri/src/commands/business_rule_archaeology/synthesis_runtime.rs @@ -554,10 +554,6 @@ pub(crate) fn resolve_trusted_provider_configuration( validate_provider_descriptor(&descriptor)?; descriptor } - "free-ai" => canonical_hosted_descriptor( - "free-ai", - "https://ai-gateway.sassmaker.com/v1/chat/completions", - ), "openai" => canonical_hosted_descriptor("openai", "https://api.openai.com/v1/responses"), "anthropic" => { canonical_hosted_descriptor("anthropic", "https://api.anthropic.com/v1/messages") @@ -572,8 +568,7 @@ pub(crate) fn resolve_trusted_provider_configuration( return Err("Hosted archaeology synthesis cannot accept a local endpoint".into()); } - let expected_cost = if user.provider_identity == "local" || user.provider_identity == "free-ai" - { + let expected_cost = if user.provider_identity == "local" { ArchaeologyCostClass::Free } else { ArchaeologyCostClass::Paid @@ -2231,7 +2226,6 @@ fn validate_provider_descriptor(descriptor: &ArchaeologyProviderDescriptor) -> R } (ArchaeologyProviderKind::Hosted, ArchaeologyNetworkScope::Remote) => { let allowed = match descriptor.provider_identity.as_str() { - "free-ai" => "https://ai-gateway.sassmaker.com/v1/chat/completions", "openai" => "https://api.openai.com/v1/responses", "anthropic" => "https://api.anthropic.com/v1/messages", "openrouter" => "https://openrouter.ai/api/v1/chat/completions", @@ -2381,7 +2375,7 @@ fn validate_selection_identity( return Err("Archaeology synthesis provider selection is invalid or unbounded".into()); } let expected_cost = match selection.provider_identity.as_str() { - "local" | "free-ai" => ArchaeologyCostClass::Free, + "local" => ArchaeologyCostClass::Free, "openai" | "anthropic" | "openrouter" => ArchaeologyCostClass::Paid, _ if descriptor.kind == ArchaeologyProviderKind::Local => ArchaeologyCostClass::Free, _ => return Err("Archaeology synthesis provider cost class is unknown".into()), diff --git a/apps/desktop/src/lib/review-service.test.ts b/apps/desktop/src/lib/review-service.test.ts index d8d70467..19c1677d 100644 --- a/apps/desktop/src/lib/review-service.test.ts +++ b/apps/desktop/src/lib/review-service.test.ts @@ -125,7 +125,7 @@ describe('buildActiveStandardsContext', () => { describe('PROVIDER_PRESETS', () => { it('exposes a base url and model for each known provider', () => { - for (const key of ['free-ai', 'anthropic', 'openai', 'openrouter']) { + for (const key of ['anthropic', 'openai', 'openrouter']) { const preset = PROVIDER_PRESETS[key]; assert.ok(preset, `missing preset for ${key}`); assert.match(preset.baseUrl, /^https:\/\//); diff --git a/apps/desktop/src/lib/review-service.ts b/apps/desktop/src/lib/review-service.ts index df4ed392..d5b3839a 100644 --- a/apps/desktop/src/lib/review-service.ts +++ b/apps/desktop/src/lib/review-service.ts @@ -125,10 +125,6 @@ export function getActiveStandardsPackId(): string | null { } export const PROVIDER_PRESETS: Record = { - 'free-ai': { - baseUrl: 'https://ai-gateway.sassmaker.com/v1', - model: 'auto', - }, anthropic: { baseUrl: 'https://api.anthropic.com/v1', model: 'claude-sonnet-4-20250514', From 855202998b56c1658b9decda22298a1b63fb5caf Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Mon, 31 Aug 2026 03:17:09 +0530 Subject: [PATCH 11/37] chore: harden repository verification tooling --- .github/dependabot.yml | 4 + .github/workflows/auto-release.yml | 24 +- .github/workflows/ci.yml | 27 +- .github/workflows/deploy-landing.yml | 21 +- .github/workflows/docs.yml | 11 +- .github/workflows/osv-offline.yml | 84 + .github/workflows/release.yml | 30 +- .github/workflows/repository-security.yml | 169 ++ .github/workflows/weekly.yml | 64 +- .gitleaksignore | 10 + .husky/pre-commit | 8 +- .husky/pre-push | 16 +- apps/desktop/src-tauri/Cargo.lock | 5 +- apps/desktop/src-tauri/Cargo.toml | 1 + apps/desktop/src-tauri/deny.toml | 40 + .../src-tauri/src/commands/agent_terminal.rs | 4 +- .../desktop/src-tauri/src/commands/history.rs | 7 +- apps/desktop/src/lib/review-service.test.ts | 56 +- apps/desktop/src/lib/review-service.ts | 67 +- apps/desktop/src/pages/Rubrics.tsx | 4 - apps/desktop/src/pages/Settings.tsx | 124 - docs-site/blume.config.ts | 4 +- docs-site/package.json | 2 +- docs-site/pnpm-lock.yaml | 2280 ++++++++++++++--- docs/architecture/data-model.md | 6 +- docs/development/performance.md | 6 +- docs/development/verification-receipts.md | 4 +- docs/knowledge/performance-lab-publication.md | 2 +- docs/knowledge/tooling-decisions.md | 63 +- docs/knowledge/tooling-documentation.md | 25 +- docs/operations/ci.md | 39 +- .../actionlint-baseline-2026-08-31.md | 45 + ...ast-grep-sarif-qualification-2026-08-31.md | 40 + .../cargo-deny-baseline-2026-08-31.md | 54 + evidence/security/osv-baseline-2026-08-31.md | 55 + .../trivy-config-qualification-2026-08-31.md | 45 + .../stryker-accounting-oracle-2026-08-31.md | 30 + knip.json | 3 + package.json | 7 + scripts/merge-landing-docs.mjs | 13 +- .../qualify-codex-accounting-oracle.test.mjs | 199 +- scripts/run-biome-sarif.mjs | 22 + scripts/run-osv-offline.mjs | 191 ++ scripts/run-osv-offline.test.mjs | 70 + scripts/stryker-accounting.config.mjs | 33 + 45 files changed, 3394 insertions(+), 620 deletions(-) create mode 100644 .github/workflows/osv-offline.yml create mode 100644 .github/workflows/repository-security.yml create mode 100644 .gitleaksignore create mode 100644 apps/desktop/src-tauri/deny.toml create mode 100644 evidence/security/actionlint-baseline-2026-08-31.md create mode 100644 evidence/security/ast-grep-sarif-qualification-2026-08-31.md create mode 100644 evidence/security/cargo-deny-baseline-2026-08-31.md create mode 100644 evidence/security/osv-baseline-2026-08-31.md create mode 100644 evidence/security/trivy-config-qualification-2026-08-31.md create mode 100644 evidence/verification/stryker-accounting-oracle-2026-08-31.md create mode 100644 scripts/run-biome-sarif.mjs create mode 100644 scripts/run-osv-offline.mjs create mode 100644 scripts/run-osv-offline.test.mjs create mode 100644 scripts/stryker-accounting.config.mjs diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dc150637..9b8b11d2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,6 +4,10 @@ updates: directory: / schedule: interval: weekly + # Let ecosystem fixes settle before routine version PRs. Dependabot + # security updates are explicitly not delayed by this setting. + cooldown: + default-days: 7 groups: ccusage-runtime: patterns: diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 224e1f98..2163a2b1 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -18,9 +18,7 @@ on: - "apps/desktop/src-tauri/tauri.conf.json" workflow_dispatch: -permissions: - contents: write - actions: write +permissions: {} concurrency: group: auto-release-${{ github.ref }} @@ -28,11 +26,16 @@ concurrency: jobs: publish: + name: Publish release and dispatch build runs-on: ubuntu-latest + permissions: + contents: write # Create the versioned GitHub release. + actions: write # Dispatch the separate signed-build workflow. steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: fetch-depth: 0 + persist-credentials: false - name: Read version from tauri.conf.json id: ver @@ -50,12 +53,13 @@ jobs: id: check env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ steps.ver.outputs.tag }} run: | set -euo pipefail - if gh release view "${{ steps.ver.outputs.tag }}" \ + if gh release view "$RELEASE_TAG" \ --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then echo "exists=true" >> "$GITHUB_OUTPUT" - echo "Release ${{ steps.ver.outputs.tag }} already exists — skipping." + echo "Release $RELEASE_TAG already exists — skipping." else echo "exists=false" >> "$GITHUB_OUTPUT" fi @@ -64,21 +68,23 @@ jobs: if: steps.check.outputs.exists != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ steps.ver.outputs.tag }} run: | set -euo pipefail - gh release create "${{ steps.ver.outputs.tag }}" \ + gh release create "$RELEASE_TAG" \ --repo "$GITHUB_REPOSITORY" \ --target "$GITHUB_SHA" \ - --title "CodeVetter ${{ steps.ver.outputs.tag }}" \ + --title "CodeVetter $RELEASE_TAG" \ --generate-notes - name: Dispatch release build workflow if: steps.check.outputs.exists != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ steps.ver.outputs.tag }} run: | set -euo pipefail gh workflow run release.yml \ --repo "$GITHUB_REPOSITORY" \ --ref main \ - -f tag="${{ steps.ver.outputs.tag }}" + -f tag="$RELEASE_TAG" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea6d5de5..69bae25f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,19 +4,29 @@ on: branches: [main] pull_request: workflow_dispatch: +permissions: {} + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: lint-and-typecheck: + name: Lint, test, and build runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: fetch-depth: 2 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v6 + persist-credentials: false + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: '22' cache: 'pnpm' - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable - name: Install Tauri Linux dependencies run: | sudo apt-get update @@ -25,7 +35,7 @@ jobs: libayatana-appindicator3-dev \ librsvg2-dev \ libxdo-dev - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: apps/desktop/src-tauri - name: Install Dependencies @@ -35,7 +45,9 @@ jobs: run: pnpm run lint - name: Fetch code-health base if: github.event_name != 'workflow_dispatch' - run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha || github.event.before || 'HEAD^' }} + env: + CODE_HEALTH_BASE: ${{ github.event.pull_request.base.sha || github.event.before || 'HEAD^' }} + run: git fetch --depth=1 origin "$CODE_HEALTH_BASE" - name: Code health env: CODE_HEALTH_BASE: ${{ github.event.pull_request.base.sha || github.event.before || 'HEAD^' }} @@ -83,7 +95,8 @@ jobs: run: | pnpm run test:ccusage-sidecar pnpm run prepare:ccusage-sidecar - src-tauri/binaries/ccusage-$(rustc -vV | sed -n 's/^host: //p') --version + CCUSAGE_TARGET="$(rustc -vV | sed -n 's/^host: //p')" + "src-tauri/binaries/ccusage-$CCUSAGE_TARGET" --version - name: Qualify CLI artifact working-directory: apps/desktop run: | diff --git a/.github/workflows/deploy-landing.yml b/.github/workflows/deploy-landing.yml index 629d8339..787eff44 100644 --- a/.github/workflows/deploy-landing.yml +++ b/.github/workflows/deploy-landing.yml @@ -7,9 +7,8 @@ name: Deploy Landing Page # apps/landing-page-astro/. on: workflow_dispatch: -permissions: - contents: read - deployments: write + +permissions: {} concurrency: group: deploy-landing-${{ github.ref }} @@ -17,16 +16,22 @@ concurrency: jobs: deploy: + name: Build and deploy landing page runs-on: ubuntu-latest timeout-minutes: 20 + permissions: + contents: read + deployments: write # Record the Cloudflare Pages deployment. steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v6 + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: "22" - cache: pnpm + package-manager-cache: false - name: Install dependencies run: pnpm install --frozen-lockfile @@ -58,7 +63,7 @@ jobs: - name: Deploy to Cloudflare Pages if: steps.cloudflare.outputs.deploy_enabled == 'true' - uses: cloudflare/wrangler-action@v3 + uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 21d75599..a59fc946 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,12 +5,19 @@ on: [push, pull_request] permissions: contents: read +concurrency: + group: docs-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: validate: + name: Validate documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: "22" - name: Validate docs (links, frontmatter, structure) diff --git a/.github/workflows/osv-offline.yml b/.github/workflows/osv-offline.yml new file mode 100644 index 00000000..26e263d0 --- /dev/null +++ b/.github/workflows/osv-offline.yml @@ -0,0 +1,84 @@ +name: OSV Offline Scan + +on: + workflow_dispatch: + +permissions: {} + +concurrency: + group: osv-offline-${{ github.ref }} + cancel-in-progress: false + +jobs: + refresh-databases: + name: Refresh OSV databases + runs-on: ubuntu-latest + steps: + - name: Download ecosystem databases + env: + OSV_CACHE_ROOT: ${{ runner.temp }}/osv-db/osv-scalibr + run: | + mkdir -p "$OSV_CACHE_ROOT/npm" "$OSV_CACHE_ROOT/crates.io" "$OSV_CACHE_ROOT/Go" + curl --fail --silent --show-error --location \ + https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip \ + --output "$OSV_CACHE_ROOT/npm/all.zip" + curl --fail --silent --show-error --location \ + https://osv-vulnerabilities.storage.googleapis.com/crates.io/all.zip \ + --output "$OSV_CACHE_ROOT/crates.io/all.zip" + curl --fail --silent --show-error --location \ + https://osv-vulnerabilities.storage.googleapis.com/Go/all.zip \ + --output "$OSV_CACHE_ROOT/Go/all.zip" + cd "$OSV_CACHE_ROOT" + sha256sum npm/all.zip crates.io/all.zip Go/all.zip > SHA256SUMS + - name: Upload database snapshot + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: osv-databases-${{ github.run_id }} + path: ${{ runner.temp }}/osv-db + retention-days: 7 + + offline-scan: + name: Scan with network-disabled mode + needs: refresh-databases + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Download database snapshot + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + with: + name: osv-databases-${{ github.run_id }} + path: ${{ runner.temp }}/osv-db + - name: Install checksum-pinned OSV-Scanner + env: + OSV_BINARY: osv-scanner_linux_amd64 + OSV_SHA256: f9f25499a2c8cc367b3af45df2ea7eeca7fbccceab9c35079968f4b3652194be + OSV_URL: https://github.com/google/osv-scanner/releases/download/v2.5.1/osv-scanner_linux_amd64 + run: | + curl --fail --silent --show-error --location "$OSV_URL" --output "$RUNNER_TEMP/$OSV_BINARY" + echo "$OSV_SHA256 $RUNNER_TEMP/$OSV_BINARY" | sha256sum --check --strict + chmod 0755 "$RUNNER_TEMP/$OSV_BINARY" + mkdir -p "$RUNNER_TEMP/osv-bin" + mv "$RUNNER_TEMP/$OSV_BINARY" "$RUNNER_TEMP/osv-bin/osv-scanner" + echo "$RUNNER_TEMP/osv-bin" >> "$GITHUB_PATH" + - name: Run offline scan + id: scan + continue-on-error: true + env: + XDG_CACHE_HOME: ${{ runner.temp }}/osv-db + run: node scripts/run-osv-offline.mjs + - name: Upload scan evidence + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: osv-offline-evidence-${{ github.run_id }} + path: artifacts/tooling/osv + if-no-files-found: error + retention-days: 30 + - name: Enforce scan result + if: always() && steps.scan.outcome == 'failure' + run: exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 235c30d4..fd596879 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,47 +10,51 @@ on: required: true type: string -permissions: - contents: write +permissions: {} + +concurrency: + group: release-${{ github.event.release.tag_name || inputs.tag || github.ref }} + cancel-in-progress: false jobs: build: + name: Build, sign, and upload desktop artifacts strategy: matrix: platform: [macos-latest] runs-on: ${{ matrix.platform }} + permissions: + contents: write # Upload signed artifacts and updater metadata. steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: # On workflow_dispatch we want the commit the tag points at, not # the head of main — checkout the tag explicitly. ref: ${{ github.event.release.tag_name || inputs.tag }} + persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 # Don't specify version here — it reads "packageManager" from # package.json (pnpm@10.33.2). Specifying both causes # "Multiple versions of pnpm specified" error. - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 22 - cache: pnpm + package-manager-cache: false - name: Setup Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable with: targets: aarch64-apple-darwin - name: Setup Bun - uses: oven-sh/setup-bun@v2 - - - name: Cache Rust - uses: Swatinem/rust-cache@v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 with: - workspaces: apps/desktop/src-tauri + no-cache: true - name: Install dependencies run: pnpm install --ignore-scripts @@ -102,7 +106,7 @@ jobs: - name: Build Tauri app id: tauri - uses: tauri-apps/tauri-action@v0 + uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_ENV_TARGET_TRIPLE: aarch64-apple-darwin diff --git a/.github/workflows/repository-security.yml b/.github/workflows/repository-security.yml new file mode 100644 index 00000000..9386106f --- /dev/null +++ b/.github/workflows/repository-security.yml @@ -0,0 +1,169 @@ +name: Repository Security + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: {} + +concurrency: + group: repository-security-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + actionlint: + name: GitHub Actions semantics + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install pinned workflow analyzers + env: + ACTIONLINT_ARCHIVE: actionlint_1.7.12_linux_amd64.tar.gz + ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 + ACTIONLINT_URL: https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz + SHELLCHECK_ARCHIVE: shellcheck-v0.11.0.linux.x86_64.tar.gz + SHELLCHECK_SHA256: b7af85e41cc99489dcc21d66c6d5f3685138f06d34651e6d34b42ec6d54fe6f6 + SHELLCHECK_URL: https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.x86_64.tar.gz + run: | + mkdir -p "$RUNNER_TEMP/workflow-tools" + curl --fail --silent --show-error --location "$ACTIONLINT_URL" --output "$RUNNER_TEMP/$ACTIONLINT_ARCHIVE" + echo "$ACTIONLINT_SHA256 $RUNNER_TEMP/$ACTIONLINT_ARCHIVE" | sha256sum --check --strict + tar -xzf "$RUNNER_TEMP/$ACTIONLINT_ARCHIVE" -C "$RUNNER_TEMP/workflow-tools" actionlint + curl --fail --silent --show-error --location "$SHELLCHECK_URL" --output "$RUNNER_TEMP/$SHELLCHECK_ARCHIVE" + echo "$SHELLCHECK_SHA256 $RUNNER_TEMP/$SHELLCHECK_ARCHIVE" | sha256sum --check --strict + tar -xzf "$RUNNER_TEMP/$SHELLCHECK_ARCHIVE" -C "$RUNNER_TEMP" + mv "$RUNNER_TEMP/shellcheck-v0.11.0/shellcheck" "$RUNNER_TEMP/workflow-tools/shellcheck" + echo "$RUNNER_TEMP/workflow-tools" >> "$GITHUB_PATH" + - name: Validate workflow syntax and shell fragments + run: | + actionlint -version + shellcheck --version + actionlint -color + + biome-sarif: + name: Biome SARIF + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # Publish the generated Biome report. + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Set up pnpm + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + - name: Set up Node.js + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: '22' + cache: pnpm + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Generate Biome SARIF + env: + BIOME_SARIF_PATH: artifacts/tooling/biome.sarif + run: pnpm run quality:sarif + - name: Upload Biome SARIF + if: always() + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4 + with: + sarif_file: artifacts/tooling/biome.sarif + category: biome + + cargo-deny: + name: Rust dependency policy + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # Publish license and source-policy findings. + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install pinned cargo-deny binary + env: + CARGO_DENY_ARCHIVE: cargo-deny-0.20.2-x86_64-unknown-linux-musl.tar.gz + CARGO_DENY_SHA256: 9f12ed4c49936e09b48bf862b595cde2fe64fcbd9d74dfacac6131ca824c8d5f + CARGO_DENY_URL: https://github.com/EmbarkStudios/cargo-deny/releases/download/0.20.2/cargo-deny-0.20.2-x86_64-unknown-linux-musl.tar.gz + run: | + curl --fail --silent --show-error --location "$CARGO_DENY_URL" --output "$RUNNER_TEMP/$CARGO_DENY_ARCHIVE" + echo "$CARGO_DENY_SHA256 $RUNNER_TEMP/$CARGO_DENY_ARCHIVE" | sha256sum --check --strict + tar -xzf "$RUNNER_TEMP/$CARGO_DENY_ARCHIVE" -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP/cargo-deny-0.20.2-x86_64-unknown-linux-musl" >> "$GITHUB_PATH" + - name: Check licenses, sources, and wildcard requirements + id: policy + continue-on-error: true + run: | + mkdir -p artifacts/tooling + cargo-deny --format sarif --manifest-path apps/desktop/src-tauri/Cargo.toml --config apps/desktop/src-tauri/deny.toml --frozen check licenses sources > artifacts/tooling/cargo-deny.sarif + cargo-deny --manifest-path apps/desktop/src-tauri/Cargo.toml --config apps/desktop/src-tauri/deny.toml --frozen check --hide-inclusion-graph bans + - name: Upload cargo-deny SARIF + if: always() + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4 + with: + sarif_file: artifacts/tooling/cargo-deny.sarif + category: cargo-deny + - name: Enforce cargo-deny result + if: always() && steps.policy.outcome == 'failure' + run: exit 1 + + gitleaks: + name: Gitleaks + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # Publish the redacted Gitleaks report. + steps: + - name: Checkout complete history + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Install pinned Gitleaks binary + env: + GITLEAKS_ARCHIVE: gitleaks_8.30.1_linux_x64.tar.gz + GITLEAKS_SHA256: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb + GITLEAKS_URL: https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz + run: | + curl --fail --silent --show-error --location "$GITLEAKS_URL" --output "$RUNNER_TEMP/$GITLEAKS_ARCHIVE" + echo "$GITLEAKS_SHA256 $RUNNER_TEMP/$GITLEAKS_ARCHIVE" | sha256sum --check --strict + mkdir -p "$RUNNER_TEMP/gitleaks-bin" + tar -xzf "$RUNNER_TEMP/$GITLEAKS_ARCHIVE" -C "$RUNNER_TEMP/gitleaks-bin" gitleaks + echo "$RUNNER_TEMP/gitleaks-bin" >> "$GITHUB_PATH" + - name: Scan repository history + id: scan + continue-on-error: true + run: >- + gitleaks git --no-banner --redact=100 --report-format sarif + --report-path "$RUNNER_TEMP/gitleaks.sarif" . + - name: Upload Gitleaks SARIF + if: always() + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4 + with: + sarif_file: ${{ runner.temp }}/gitleaks.sarif + category: gitleaks + - name: Enforce Gitleaks result + if: always() && steps.scan.outcome == 'failure' + run: exit 1 + + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + security-events: write # zizmor-action uploads its SARIF report. + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Audit GitHub Actions + uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml index c0f7cecb..cb36ed8e 100644 --- a/.github/workflows/weekly.yml +++ b/.github/workflows/weekly.yml @@ -4,24 +4,35 @@ on: - cron: '0 9 * * 1' workflow_dispatch: +permissions: {} + +concurrency: + group: weekly-quality-${{ github.ref }} + cancel-in-progress: true + jobs: quality: + name: Run weekly quality canary runs-on: ubuntu-latest timeout-minutes: 20 permissions: contents: read steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false - name: Record source revision id: rev run: | - echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - echo "short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" - echo "ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + { + echo "sha=$(git rev-parse HEAD)" + echo "short=$(git rev-parse --short HEAD)" + echo "ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + } >> "$GITHUB_OUTPUT" - - uses: actions/setup-node@v6 + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: '22' @@ -72,24 +83,29 @@ jobs: - name: Emit canary evidence if: always() + env: + JOB_STATUS: ${{ job.status }} + SOURCE_REVISION: ${{ steps.rev.outputs.sha }} + SOURCE_REVISION_SHORT: ${{ steps.rev.outputs.short }} + STARTED_AT: ${{ steps.rev.outputs.ts }} run: | set -euo pipefail mkdir -p canary-out # The conclusion is only known after the quality step; read it # from the job status env that GitHub sets for `if: always()` steps. # We treat any non-success quality step as a failure. - CONCLUSION="${{ job.status }}" + CONCLUSION="$JOB_STATUS" # `job.status` is the *job* status at the point this step starts; # because this step runs with `if: always()`, the prior step's # failure has already propagated to the job status. cat > canary-out/canary-evidence.json <> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "| Field | Value |" >> "$GITHUB_STEP_SUMMARY" - echo "|---|---|" >> "$GITHUB_STEP_SUMMARY" - echo "| Revision | \`${{ steps.rev.outputs.short }}\` |" >> "$GITHUB_STEP_SUMMARY" - echo "| Started | ${{ steps.rev.outputs.ts }} |" >> "$GITHUB_STEP_SUMMARY" - echo "| Conclusion | ${CONCLUSION} |" >> "$GITHUB_STEP_SUMMARY" - echo "| Timeout | 20 minutes |" >> "$GITHUB_STEP_SUMMARY" - echo "| Run | [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) |" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "If this run failed, the previous failed run's conclusion + URL are the unresolved failure evidence. Foundry reads this artifact to compute freshness against the 8-day window." >> "$GITHUB_STEP_SUMMARY" + { + echo "### Weekly canary evidence" + echo "" + echo "| Field | Value |" + echo "|---|---|" + echo "| Revision | \`$SOURCE_REVISION_SHORT\` |" + echo "| Started | $STARTED_AT |" + echo "| Conclusion | ${CONCLUSION} |" + echo "| Timeout | 20 minutes |" + echo "| Run | [$GITHUB_RUN_ID]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID) |" + echo "" + echo "If this run failed, the previous failed run's conclusion + URL are the unresolved failure evidence. Foundry reads this artifact to compute freshness against the 8-day window." + } >> "$GITHUB_STEP_SUMMARY" cat canary-out/canary-evidence.json - name: Upload canary evidence if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: canary-evidence path: canary-out/canary-evidence.json diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 00000000..2bd3815f --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,10 @@ +# Exact historical false positives retained for auditability. Each entry is +# scoped to one commit, path, rule, and line; future findings in the same files +# remain visible. +# PostHog browser ingestion key, public by design (see tooling docs). +930c8952a3c80edd61c06c2f49106e616c8dde9a:apps/desktop/src/lib/analytics.ts:generic-api-key:25 +39f414d063cac9ebe9258c69a81c05348f3e4b89:apps/desktop/src/lib/analytics.ts:generic-api-key:25 +# Historical Foundry public project identifier. +81df29eebf1d724d2939cbfd8123b86e9848a940:foundry.json:generic-api-key:4 +# Historical database migration fixture value. +ac5d18ee9b33811accec2df5d7bc33b69f9bb6c3:apps/desktop/src-tauri/src/db/schema.rs:generic-api-key:23 diff --git a/.husky/pre-commit b/.husky/pre-commit index 2312dc58..f46c0171 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,7 @@ -npx lint-staged +pnpm exec lint-staged + +if command -v gitleaks >/dev/null 2>&1; then + pnpm run quality:secrets:staged +else + echo "gitleaks is not installed; repository security CI will enforce the history scan" >&2 +fi diff --git a/.husky/pre-push b/.husky/pre-push index b0c49490..f7bb930f 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,14 +1,18 @@ -# Abort push if lint fails or a known secret pattern leaks into tracked files. -# -# NOTE: foundry.json is excluded below because it already contains a committed -# projectKey (pk_...) that predates the pk_ pattern. Remove that exclusion once -# the key is rotated and moved out of the tracked file. +# Abort push if lint fails or Gitleaks finds a secret in repository history. +# Keep the narrow regex fallback for contributors who do not have Gitleaks. set -e if [ -f package.json ] && grep -q '"lint"' package.json; then - npm run lint || { echo "lint failed — fix before pushing" >&2; exit 1; } + pnpm run lint || { echo "lint failed — fix before pushing" >&2; exit 1; } fi +if command -v gitleaks >/dev/null 2>&1; then + pnpm run quality:secrets + exit 0 +fi + +echo "gitleaks is not installed; using the limited tracked-file fallback" >&2 + SECRETS=$(git ls-files -z 2>/dev/null \ | xargs -0 grep -lE \ 'sk-(proj-|ant-)?[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|gho_[A-Za-z0-9]{36}|AIzaSy[A-Za-z0-9_-]{33}|xoxb-[A-Za-z0-9-]+|pk_[A-Za-z0-9]{32,}|-----BEGIN (RSA |EC )?PRIVATE KEY-----' 2>/dev/null \ diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock index a605276f..dbb15484 100644 --- a/apps/desktop/src-tauri/Cargo.lock +++ b/apps/desktop/src-tauri/Cargo.lock @@ -1278,11 +1278,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 22ba15c3..07699fea 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -2,6 +2,7 @@ name = "codevetter-desktop" version = "0.1.0" edition = "2021" +publish = false description = "CodeVetter Desktop — code review + agent management" default-run = "codevetter-desktop" diff --git a/apps/desktop/src-tauri/deny.toml b/apps/desktop/src-tauri/deny.toml new file mode 100644 index 00000000..b8813bbe --- /dev/null +++ b/apps/desktop/src-tauri/deny.toml @@ -0,0 +1,40 @@ +[graph] +targets = ["aarch64-apple-darwin"] +all-features = false +no-default-features = false + +[licenses] +allow = [ + "0BSD", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSD-2-Clause", + "BSD-3-Clause", + "BSL-1.0", + "CC0-1.0", + "CDLA-Permissive-2.0", + "ISC", + "MIT", + "MIT-0", + "MPL-2.0", + "Unicode-3.0", + "Unlicense", + "Zlib", +] +confidence-threshold = 0.8 + +[licenses.private] +ignore = true + +[bans] +multiple-versions = "warn" +wildcards = "deny" +highlight = "simplest-path" +workspace-default-features = "allow" +external-default-features = "allow" + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] diff --git a/apps/desktop/src-tauri/src/commands/agent_terminal.rs b/apps/desktop/src-tauri/src/commands/agent_terminal.rs index 54c91aba..9cbe0a40 100644 --- a/apps/desktop/src-tauri/src/commands/agent_terminal.rs +++ b/apps/desktop/src-tauri/src/commands/agent_terminal.rs @@ -570,9 +570,7 @@ fn start_agent_terminal_impl( ) { Ok(result) => return Ok(result), Err(error) => { - eprintln!( - "Codex app-server unavailable for {session_id}; falling back to PTY: {error}" - ); + eprintln!("Codex app-server unavailable; falling back to PTY: {error}"); } } } diff --git a/apps/desktop/src-tauri/src/commands/history.rs b/apps/desktop/src-tauri/src/commands/history.rs index 004cf20b..24c30f15 100644 --- a/apps/desktop/src-tauri/src/commands/history.rs +++ b/apps/desktop/src-tauri/src/commands/history.rs @@ -1565,12 +1565,11 @@ fn upsert_adapter_summary_session( let archive_messages = summary.archive_messages.clone(); let parse_warnings = summary.parse_warnings.clone(); - for warning in &summary.parse_warnings { + if !summary.parse_warnings.is_empty() { log::warn!( - "{} session adapter warning for {}: {}", + "{} session adapter reported {} parse warning(s)", summary.adapter_id, - source_ref, - warning + summary.parse_warnings.len() ); } diff --git a/apps/desktop/src/lib/review-service.test.ts b/apps/desktop/src/lib/review-service.test.ts index 19c1677d..647c3888 100644 --- a/apps/desktop/src/lib/review-service.test.ts +++ b/apps/desktop/src/lib/review-service.test.ts @@ -7,7 +7,6 @@ import { getActiveStandardsPack, getStandardsPacks, loadReviewConfig, - PROVIDER_PRESETS, type ReviewConfig, saveReviewConfig, } from './review-service'; @@ -29,10 +28,8 @@ class MemoryStorage { } const validConfig: ReviewConfig = { - gatewayBaseUrl: 'https://gateway.example/v1', - gatewayApiKey: 'sk-test', - gatewayModel: 'auto', - reviewTone: 'direct', + activeStandardsPack: 'product-safety', + customRules: ['Check authorization'], }; beforeEach(() => { @@ -45,20 +42,48 @@ describe('loadReviewConfig', () => { assert.equal(loadReviewConfig(), null); }); - it('returns null when required credentials are missing', () => { - saveReviewConfig({ ...validConfig, gatewayApiKey: '' }); - assert.equal(loadReviewConfig(), null); - }); - it('returns null on malformed JSON', () => { localStorage.setItem('codevetter_review_config', '{not json'); assert.equal(loadReviewConfig(), null); + assert.equal(localStorage.getItem('codevetter_review_config'), null); }); it('round-trips a valid config', () => { saveReviewConfig(validConfig); assert.deepEqual(loadReviewConfig(), validConfig); }); + + it('migrates legacy provider config without retaining the credential', () => { + localStorage.setItem( + 'codevetter_review_config', + JSON.stringify({ + ...validConfig, + gatewayApiKey: 'sk-legacy-secret', + gatewayBaseUrl: 'https://api.example.test/v1', + gatewayModel: 'legacy-model', + reviewTone: 'direct', + }) + ); + + assert.deepEqual(loadReviewConfig(), validConfig); + const stored = localStorage.getItem('codevetter_review_config') ?? ''; + assert.equal(stored.includes('sk-legacy-secret'), false); + assert.equal(stored.includes('gatewayApiKey'), false); + assert.equal(stored.includes('gatewayBaseUrl'), false); + assert.equal(stored.includes('gatewayModel'), false); + }); + + it('persists only allowlisted review-standard fields', () => { + saveReviewConfig({ + ...validConfig, + gatewayApiKey: 'sk-should-not-persist', + } as ReviewConfig & { gatewayApiKey: string }); + + const stored = localStorage.getItem('codevetter_review_config'); + assert.ok(stored); + assert.deepEqual(JSON.parse(stored), validConfig); + assert.equal(stored.includes('sk-should-not-persist'), false); + }); }); describe('getStandardsPacks', () => { @@ -122,14 +147,3 @@ describe('buildActiveStandardsContext', () => { assert.equal((context.match(/Custom rule:/g) ?? []).length, 1); }); }); - -describe('PROVIDER_PRESETS', () => { - it('exposes a base url and model for each known provider', () => { - for (const key of ['anthropic', 'openai', 'openrouter']) { - const preset = PROVIDER_PRESETS[key]; - assert.ok(preset, `missing preset for ${key}`); - assert.match(preset.baseUrl, /^https:\/\//); - assert.ok(preset.model.length > 0); - } - }); -}); diff --git a/apps/desktop/src/lib/review-service.ts b/apps/desktop/src/lib/review-service.ts index d5b3839a..1905812c 100644 --- a/apps/desktop/src/lib/review-service.ts +++ b/apps/desktop/src/lib/review-service.ts @@ -1,13 +1,6 @@ -/** - * Review config persistence and provider presets. - * Used by the Settings page to configure AI provider credentials. - */ +/** Review-standards persistence. Provider credentials are never stored here. */ export interface ReviewConfig { - gatewayBaseUrl: string; - gatewayApiKey: string; - gatewayModel: string; - reviewTone: string; customRules?: string[]; activeStandardsPack?: string; standardsPacks?: StandardsPack[]; @@ -55,20 +48,57 @@ export const DEFAULT_STANDARDS_PACKS: StandardsPack[] = [ }, ]; +function isStandardsPack(value: unknown): value is StandardsPack { + if (!value || typeof value !== 'object' || Array.isArray(value)) return false; + const candidate = value as Partial; + return ( + typeof candidate.id === 'string' && + typeof candidate.name === 'string' && + typeof candidate.focus === 'string' && + Array.isArray(candidate.checks) && + candidate.checks.every((check) => typeof check === 'string') + ); +} + +function sanitizeReviewConfig(value: unknown): ReviewConfig | null { + if (!value || typeof value !== 'object' || Array.isArray(value)) return null; + const candidate = value as Partial; + const config: ReviewConfig = {}; + + if (Array.isArray(candidate.customRules)) { + config.customRules = candidate.customRules.filter( + (rule): rule is string => typeof rule === 'string' + ); + } + if (typeof candidate.activeStandardsPack === 'string') { + config.activeStandardsPack = candidate.activeStandardsPack; + } + if (Array.isArray(candidate.standardsPacks)) { + config.standardsPacks = candidate.standardsPacks.filter(isStandardsPack); + } + return config; +} + export function loadReviewConfig(): ReviewConfig | null { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return null; - const config = JSON.parse(raw) as ReviewConfig; - if (!config.gatewayApiKey || !config.gatewayBaseUrl) return null; + const config = sanitizeReviewConfig(JSON.parse(raw)); + if (!config) { + localStorage.removeItem(STORAGE_KEY); + return null; + } + const sanitized = JSON.stringify(config); + if (sanitized !== raw) localStorage.setItem(STORAGE_KEY, sanitized); return config; } catch { + localStorage.removeItem(STORAGE_KEY); return null; } } export function saveReviewConfig(config: ReviewConfig): void { - localStorage.setItem(STORAGE_KEY, JSON.stringify(config)); + localStorage.setItem(STORAGE_KEY, JSON.stringify(sanitizeReviewConfig(config) ?? {})); } export function getStandardsPacks(config: ReviewConfig | null): StandardsPack[] { @@ -123,18 +153,3 @@ export function getActiveStandardsPackId(): string | null { if (!config?.activeStandardsPack) return null; return getActiveStandardsPack(config).id; } - -export const PROVIDER_PRESETS: Record = { - anthropic: { - baseUrl: 'https://api.anthropic.com/v1', - model: 'claude-sonnet-4-20250514', - }, - openai: { - baseUrl: 'https://api.openai.com/v1', - model: 'gpt-4o', - }, - openrouter: { - baseUrl: 'https://openrouter.ai/api/v1', - model: 'anthropic/claude-sonnet-4-20250514', - }, -}; diff --git a/apps/desktop/src/pages/Rubrics.tsx b/apps/desktop/src/pages/Rubrics.tsx index 41eaada9..3e2bdc72 100644 --- a/apps/desktop/src/pages/Rubrics.tsx +++ b/apps/desktop/src/pages/Rubrics.tsx @@ -27,10 +27,6 @@ import { getStandardsPackUsage, isTauriAvailable } from '@/lib/tauri-ipc'; function fallbackConfig(): ReviewConfig { return { - gatewayBaseUrl: '', - gatewayApiKey: '', - gatewayModel: 'auto', - reviewTone: 'direct', activeStandardsPack: DEFAULT_STANDARDS_PACKS[0].id, standardsPacks: [], }; diff --git a/apps/desktop/src/pages/Settings.tsx b/apps/desktop/src/pages/Settings.tsx index 0d0eca8d..8557710d 100644 --- a/apps/desktop/src/pages/Settings.tsx +++ b/apps/desktop/src/pages/Settings.tsx @@ -6,12 +6,6 @@ import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Separator } from '@/components/ui/separator'; -import { - loadReviewConfig, - PROVIDER_PRESETS, - type ReviewConfig, - saveReviewConfig, -} from '@/lib/review-service'; import type { GitHubAuthStatus, LinearUser, @@ -590,48 +584,6 @@ export default function Settings() { const [claudeCodePath, setClaudeCodePath] = usePref('claude_cli_path', ''); const [codexPath, setCodexPath] = usePref('codex_cli_path', ''); - // AI Provider - const [aiProvider, setAiProvider] = useState('anthropic'); - const [aiBaseUrl, setAiBaseUrl] = useState(''); - const [aiApiKey, setAiApiKey] = useState(''); - const [aiModel, setAiModel] = useState(''); - const [aiConfigSaved, setAiConfigSaved] = useState(false); - - useEffect(() => { - const existing = loadReviewConfig(); - if (existing) { - setAiBaseUrl(existing.gatewayBaseUrl); - setAiApiKey(existing.gatewayApiKey); - setAiModel(existing.gatewayModel); - // Detect provider from URL - if (existing.gatewayBaseUrl.includes('anthropic')) setAiProvider('anthropic'); - else if (existing.gatewayBaseUrl.includes('openai.com')) setAiProvider('openai'); - else if (existing.gatewayBaseUrl.includes('openrouter')) setAiProvider('openrouter'); - else setAiProvider('custom'); - } - }, []); - - function handleProviderChange(provider: string) { - setAiProvider(provider); - setAiConfigSaved(false); - if (provider !== 'custom' && PROVIDER_PRESETS[provider]) { - setAiBaseUrl(PROVIDER_PRESETS[provider].baseUrl); - setAiModel(PROVIDER_PRESETS[provider].model); - } - } - - function handleSaveAiConfig() { - const config: ReviewConfig = { - gatewayBaseUrl: aiBaseUrl, - gatewayApiKey: aiApiKey, - gatewayModel: aiModel, - reviewTone: defaultTone, - }; - saveReviewConfig(config); - setAiConfigSaved(true); - setTimeout(() => setAiConfigSaved(false), 2000); - } - // Notifications const [notifyReviewDone, toggleNotifyReviewDone] = useBoolPref('notify_review_done', true); const [notifyAgentError, toggleNotifyAgentError] = useBoolPref('notify_agent_error', true); @@ -1500,82 +1452,6 @@ export default function Settings() {

    - -

    - AI Provider -

    -
    - - - - - { - setAiApiKey(v); - setAiConfigSaved(false); - }} - /> - - {aiProvider === 'custom' && ( - <> - - { - setAiBaseUrl(v); - setAiConfigSaved(false); - }} - /> - - )} - - - - { - setAiModel(v); - setAiConfigSaved(false); - }} - /> - -
    - - {!aiApiKey && ( - API key required to run reviews - )} -
    -
    ); diff --git a/docs-site/blume.config.ts b/docs-site/blume.config.ts index be13b8a7..0fd18e2d 100644 --- a/docs-site/blume.config.ts +++ b/docs-site/blume.config.ts @@ -29,7 +29,9 @@ export default defineConfig({ }, theme: { - accent: 'amber', // matches the product's warm amber accent (#d4a039) + // Use the product token directly. Blume 1.5 no longer ships an "amber" + // preset, and treating the unknown slug as a CSS color breaks OG rendering. + accent: '#d4a039', radius: 'md', mode: 'system', fonts: { diff --git a/docs-site/package.json b/docs-site/package.json index b9e6dce5..5746bd9e 100644 --- a/docs-site/package.json +++ b/docs-site/package.json @@ -8,6 +8,6 @@ "validate": "blume validate" }, "dependencies": { - "blume": "1.0.4" + "blume": "1.5.3" } } diff --git a/docs-site/pnpm-lock.yaml b/docs-site/pnpm-lock.yaml index d7c32fe2..003aab9c 100644 --- a/docs-site/pnpm-lock.yaml +++ b/docs-site/pnpm-lock.yaml @@ -9,26 +9,26 @@ importers: .: dependencies: blume: - specifier: 1.0.4 - version: 1.0.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@shikijs/themes@4.3.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@vercel/functions@3.7.5)(esbuild@0.28.1)(prettier@3.9.5)(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(yaml@2.9.0) + specifier: 1.5.3 + version: 1.5.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@shikijs/themes@4.3.1)(@types/node@26.4.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@vercel/functions@3.7.5)(csstype@3.2.3)(esbuild@0.28.1)(prettier@3.9.5)(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(yaml@2.9.0) packages: - '@ai-sdk/gateway@2.0.115': - resolution: {integrity: sha512-BLN5eLjzg6mKkbxAf6oP8Z7mEyRihrDzBJr7QlNPZTFvgZJ15peoflUJuskTCZFaisqelj1P4Se1pHY8QBBgBA==} - engines: {node: '>=18'} + '@ai-sdk/gateway@4.0.69': + resolution: {integrity: sha512-W5MMdyqsaziQy/A4kxlK74iEQ+NuO6OaszH32cEQpUgBW0o15S2fAdP0aYSH2/5lrVZSMXLQLCzuMkRGHBua3A==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@3.0.30': - resolution: {integrity: sha512-NCJ9JKow5ENAgEZxzvEvF20thwDiH+hutvzmrUDbloRX0azpJHNst8+7pZIVryYhLM9wgpT5/ShTSjPTFhkxEQ==} - engines: {node: '>=18'} + '@ai-sdk/provider-utils@5.0.34': + resolution: {integrity: sha512-tRBdgRcys/4d8wyQdOdyYScq1AxfMdMd0hIlwolxJKVIbBwXUgClZuQT0VIsz4e7pylY8FE6utYCCZ494UAMJQ==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@2.0.3': - resolution: {integrity: sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww==} - engines: {node: '>=18'} + '@ai-sdk/provider@4.0.9': + resolution: {integrity: sha512-XnGXPWiBIfqjsVEud5pOaVneRByJQOu2sYNwlSVJTPCvakdCDkVuYKKfNuStkIpMUYl7JIkBZGBx+B5YfNeVjA==} + engines: {node: '>=22'} '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} @@ -168,6 +168,15 @@ packages: '@astrojs/yaml2ts@0.2.4': resolution: {integrity: sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==} + '@asyncapi/converter@2.0.2': + resolution: {integrity: sha512-tFvT2ijEriTe9CPCMP32fh2VPzdQiUupK63MyW7UBDCtENANPZJ0aboZuVPhgBhmqo2TxcHWbZANpmXc5sEFiw==} + + '@asyncapi/parser@3.6.3': + resolution: {integrity: sha512-MUC8xIUMcS2qNvqrqyx/ie0txu3d/OdIsrXs7UCzawdyR6P07gh35DpOqPz/z57s1UA3vERVpcheZYl3h8cVtw==} + + '@asyncapi/specs@6.11.1': + resolution: {integrity: sha512-A3WBLqAKGoJ2+6FWFtpjBlCQ1oFCcs4GxF7zsIGvNqp/klGUHjlA3aAcZ9XMMpLGE8zPeYDz2x9FmO6DSuKraQ==} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -345,6 +354,9 @@ packages: '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} @@ -523,152 +535,161 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} + engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} + engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} + engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} + engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} + engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} + engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -692,6 +713,24 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsep-plugin/assignment@1.3.0': + resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/regex@1.0.4': + resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/ternary@1.1.4': + resolution: {integrity: sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + '@mapbox/node-pre-gyp@2.0.3': resolution: {integrity: sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==} engines: {node: '>=18'} @@ -700,8 +739,8 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} - '@mermaid-js/parser@1.2.0': - resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} + '@mermaid-js/parser@1.2.1': + resolution: {integrity: sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==} '@modelcontextprotocol/sdk@1.29.0': resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} @@ -719,9 +758,11 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} + '@nodable/entities@3.0.0': + resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} + + '@openapi-contrib/openapi-schema-to-json-schema@3.2.0': + resolution: {integrity: sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==} '@orama/orama@3.1.18': resolution: {integrity: sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==} @@ -950,6 +991,9 @@ packages: resolution: {integrity: sha512-Sc1TkcwGV6aVCO51AyKeaGiP8gpwAHxEtO5d3tZzPV+KsnlC/YokQxFxwBrbIXw73k9hmcExnJyGu3k5i6n6VA==} engines: {node: '>=20'} + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + '@shikijs/core@4.3.1': resolution: {integrity: sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==} engines: {node: '>=20'} @@ -998,6 +1042,75 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@stoplight/better-ajv-errors@1.0.3': + resolution: {integrity: sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==} + engines: {node: ^12.20 || >= 14.13} + peerDependencies: + ajv: '>=8' + + '@stoplight/json-ref-readers@1.2.2': + resolution: {integrity: sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==} + engines: {node: '>=8.3.0'} + + '@stoplight/json-ref-resolver@3.1.6': + resolution: {integrity: sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==} + engines: {node: '>=8.3.0'} + + '@stoplight/json@3.21.0': + resolution: {integrity: sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g==} + engines: {node: '>=8.3.0'} + + '@stoplight/ordered-object-literal@1.0.5': + resolution: {integrity: sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==} + engines: {node: '>=8'} + + '@stoplight/path@1.3.2': + resolution: {integrity: sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==} + engines: {node: '>=8'} + + '@stoplight/spectral-core@1.23.1': + resolution: {integrity: sha512-VLC8OhpO/pMJKb6IHhurxJjXO1qB56Ng1unIb8b+hNxdw0+SEcASvmR+RpjfHYX/jv/DfSaA1x8QhFBJBmqBOQ==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-formats@1.8.5': + resolution: {integrity: sha512-xaC0rCH0p7/bzNJsz+JgLSj+Cp6uwYGWpePQxdLkF2G6a8Zyp3OyS7umkGYNiimEwKrOjvCNNTFJpeuiENZSBA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-functions@1.10.5': + resolution: {integrity: sha512-vDCd0NJ93715bcUpZZ5vNHiyxd4cgHF6tuXsDiXOXKAByg+I1fR5/dMijEo6Ce1Lz95a+RZ22JKYhF1YuzVvuA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-parsers@1.0.5': + resolution: {integrity: sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-ref-resolver@1.0.5': + resolution: {integrity: sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-runtime@1.1.6': + resolution: {integrity: sha512-Y8rEDyMN4bSMJCrDs2shdcVHYyCnH3FvXRP4dBhha4Z8iJv+JPp7KqOV/hwVB/hWFC209upiwj2oDmLfR0qCDg==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/types@13.20.0': + resolution: {integrity: sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==} + engines: {node: ^12.20 || >=14.13} + + '@stoplight/types@13.6.0': + resolution: {integrity: sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==} + engines: {node: ^12.20 || >=14.13} + + '@stoplight/types@14.1.1': + resolution: {integrity: sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==} + engines: {node: ^12.20 || >=14.13} + + '@stoplight/yaml-ast-parser@0.0.50': + resolution: {integrity: sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==} + + '@stoplight/yaml@4.3.0': + resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} + engines: {node: '>=10.8'} + '@tailwindcss/node@4.3.3': resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} @@ -1097,71 +1210,86 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@takumi-rs/core-darwin-arm64@1.8.7': - resolution: {integrity: sha512-an8vc2B/Qf9vo3uwDmJ6WKazpMBpDruRy/kXr+x98GfIkzMHDEUy+9EgNfUYxprtZo49cJruXrVwzxsqqW+1tA==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-darwin-arm64@2.13.3': + resolution: {integrity: sha512-tjaSLf6rSryeT/QNXkgBzzViBiad4BK5fS6drnYyWkIlbX+vc02Pmd/ZGtKuKWDJ6M7rk+kX+ymnTyzqozPlSQ==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@takumi-rs/core-darwin-x64@1.8.7': - resolution: {integrity: sha512-q0xQxmb4y4EwYK3ILMBvWvqLGX8gxsqcEas0zOwNbPnxBLTQFF8KVH4jXaKXLUFwj020lu8nsf1BNvLIkJMo3Q==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-darwin-x64@2.13.3': + resolution: {integrity: sha512-3UuW4Jh+8clca2UHyTftHu4GgPUs57Go//A5KYXzJMBNh0/Z5DDqtukGXWhgDztWCRxfxC/MD9G4z3ExN8FwIA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@takumi-rs/core-linux-arm64-gnu@1.8.7': - resolution: {integrity: sha512-uZ6l792M4Mc06XG+m+8RarTgdR5o1ZLjncULKUEPAw3nBKhRbJ50Re3aLM4n4+p25iOj1oL9QwEZRcaJn8u/Ag==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-linux-arm64-gnu@2.13.3': + resolution: {integrity: sha512-SMOUHOWA3ZrrOcdzMjBMf/P2ZlSLaYx/AYzgFQqocDKKJHg5PMPq12d0oc855KLgeCP5qF3RO57v1C28VallUQ==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] libc: [glibc] - '@takumi-rs/core-linux-arm64-musl@1.8.7': - resolution: {integrity: sha512-akVy80ztI2as/b0n0EVCfHNKhibp+Od4ioP49PtX2rQMi6KDtqv3aoCIjaBls8oxzv+4x77/2U9fsWIO2XZQjg==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-linux-arm64-musl@2.13.3': + resolution: {integrity: sha512-Ncq2dbrWr9fsL8Cb4bWwBwYx7gXFy+AaMUx4zU7I0nxBkereCtNC4UMc3RY09OElPfwv1qpKtWYB6A+CJQRW5w==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] libc: [musl] - '@takumi-rs/core-linux-x64-gnu@1.8.7': - resolution: {integrity: sha512-SV1YesGs/HfC1CMpSF5SqS6KbL4hDLvD2m54JcfB+X/0xatmo/CS2XleqoIXhSmR+95X8Of5g2vg1a4TAdpcug==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-linux-x64-gnu@2.13.3': + resolution: {integrity: sha512-0K9kKFxDKnSs1R62MZiu2J/kRabkGQ8W8TN/t0fX9/lGS2Lv76aMZ/EYIZFf0wPSazy/706ybavBU05l+gH5zw==} + engines: {node: '>=18'} cpu: [x64] os: [linux] libc: [glibc] - '@takumi-rs/core-linux-x64-musl@1.8.7': - resolution: {integrity: sha512-zprYQ/ivPDmYX4tFaONiAzoDJNTANA3kqfIe7SMHZ2YhkfEhYnr3PE3XtBytgB4W/VlTc9WQLyD77SKwPnPuFw==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-linux-x64-musl@2.13.3': + resolution: {integrity: sha512-+c7SAdhZe3kgVmyATrwkPmWJEn+taDeV3eLnVjziSF92hhpVrKbe3DTEzCCvyqvoZ8e0C0ibC44Npe1A4FORug==} + engines: {node: '>=18'} cpu: [x64] os: [linux] libc: [musl] - '@takumi-rs/core-win32-arm64-msvc@1.8.7': - resolution: {integrity: sha512-2S5YcgIj/c0E3sLzWDcxzlXoPvEjR2xC1v7yODe0CvimlRx95Df6rJ9idTS6E1KNUjDaTfqxTr8Ud4gWqYrrPg==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-win32-arm64-msvc@2.13.3': + resolution: {integrity: sha512-jFv8+DT3Rz+9xeduDoMKNGHNB+ZHEwBloJh9iTU6waiaArwG2WoJ0JF2KRoDPECdvk52W9IR+HelbDtR/WIHoQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@takumi-rs/core-win32-x64-msvc@1.8.7': - resolution: {integrity: sha512-sooiM6fL0JN597ciNAFVp9F7YxCW+8hXpwu/7wRMRo0TGQ/KMi1Y94tf1FUu2csCHN0pgy9a3y09y8+qnw3FTw==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core-win32-x64-msvc@2.13.3': + resolution: {integrity: sha512-NehYZvabxyMHCEEKaJGbcq3oLpbQE+Gmo6TcOwmxbBG0k1bR6Q9uka+cqgQWKqA3D7uipBOXrDcir5HqjqvUuQ==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@takumi-rs/core@1.8.7': - resolution: {integrity: sha512-Ia8I3vg0VAQPnzHiYRrd18UgZbIj3X0zMwWMjuTm3yb2TBH1988lPwnnlf+eAfYMwZrutNiXLLtgEnkzsm2gMw==} - engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} + '@takumi-rs/core@2.13.3': + resolution: {integrity: sha512-b0xSv0e4N7MrG3GoSl12Ewj3UB/JoTB6GwfX/7i1jYKlnGV66+a+9NrSzCxfjcPeUBaSPIzaxQ+C6L1256ClIA==} + engines: {node: '>=18'} + peerDependencies: + csstype: '*' + peerDependenciesMeta: + csstype: + optional: true - '@takumi-rs/helpers@1.8.7': - resolution: {integrity: sha512-5dSR9W8msQ7Asp4TCvUUB9Bt8yLgIc2ASjWtEG4Jn9xSuAVJLWFZ21Xl8HShW5GE6SV5aCCM9BL0NA9YuBWIJw==} + '@takumi-rs/helpers@2.13.3': + resolution: {integrity: sha512-9ojGETH+sJuszHra6llslChQ0TD8v5MvsTbfkJiIwvxT+ekGOaafi7HEqpqY7diAsYnGE/sBhRyuUIQ6IbvusA==} + engines: {node: '>=18'} peerDependencies: - react: ^19.2.5 - react-dom: ^18.0.0 || ^19.0.0 + preact: ^10.0.0 + react: ^18.0.0 || ^19.0.0 peerDependenciesMeta: + preact: + optional: true react: optional: true - react-dom: + + '@takumi-rs/wasm@2.13.3': + resolution: {integrity: sha512-ev37eFGozTbram8nmf0APE7afZpBMIDLdVUXmzQ5sP0SDuKzDUHV5WOhwbWWA8p0P664EgQ/UV8K8nOyEiqC7g==} + engines: {node: '>=18'} + peerDependencies: + csstype: '*' + peerDependenciesMeta: + csstype: optional: true '@tybys/wasm-util@0.10.3': @@ -1275,6 +1403,9 @@ packages: '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/es-aggregate-error@1.0.6': + resolution: {integrity: sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1287,6 +1418,9 @@ packages: '@types/hast@3.0.5': resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -1299,6 +1433,9 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@26.4.0': + resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -1316,6 +1453,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/urijs@1.19.26': + resolution: {integrity: sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg==} + '@typescript/vfs@1.6.4': resolution: {integrity: sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==} peerDependencies: @@ -1406,8 +1546,8 @@ packages: engines: {node: '>=20'} hasBin: true - '@vercel/oidc@3.1.0': - resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} engines: {node: '>= 20'} '@vercel/oidc@3.8.0': @@ -1449,6 +1589,9 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + '@workflow/serde@4.1.0': + resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} engines: {node: ^18.17.0 || >=20.5.0} @@ -1480,9 +1623,9 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ai@5.0.216: - resolution: {integrity: sha512-W1QgTQS/2itHasAjqx0CVWfRojmnu1V4tJbqaHJf5hXckAZa9IrPDS9VNWBSgZjBhBE9//5M+D4FKdvAGcQBEw==} - engines: {node: '>=18'} + ai@7.0.85: + resolution: {integrity: sha512-HVtPz0qLbTUad+QBnWWReIUmwk+U4PcRENMx+9PsHGVoinoc5CLDiVjNR+VBXTKOSaNgce8kSU/Rtbb4kjZsSw==} + engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -1494,6 +1637,19 @@ packages: ajv: optional: true + ajv-errors@3.0.0: + resolution: {integrity: sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==} + peerDependencies: + ajv: ^8.0.1 + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -1521,6 +1677,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1529,6 +1689,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + anynum@1.0.1: + resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1539,9 +1702,17 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + array-iterate@2.0.1: resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -1556,12 +1727,24 @@ packages: '@astrojs/markdown-remark': optional: true + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + async-sema@3.1.1: resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + avsc@5.7.9: + resolution: {integrity: sha512-yOA4wFeI7ET3v32Di/sUybQ+ttP20JHSW3mxLuNGeO0uD6PPcvLrIQXSvy/rhJOWU5JrYh7U4OHplWMmtAtjMg==} + engines: {node: '>=0.11'} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -1587,21 +1770,21 @@ packages: bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - blume@1.0.4: - resolution: {integrity: sha512-sJmmm3MJ1am+4ubZPXlrgE4rx4vn+N92Aqyo9zxDL5+T4a3w5Hpv0FqxF3iMlv6qYOHp+Ts7d72rx348SePxSg==} + blume@1.5.3: + resolution: {integrity: sha512-8oaUwDtMcEWdCxnUmnPnw6SJol93EAavUq7ihFcr6ur2mCPcdkMjMzphM5Y3qIAexwy2J/bsQKRzAWKqHqIUSw==} engines: {node: '>=22.12.0'} hasBin: true peerDependencies: - '@ai-sdk/openai-compatible': ^1.0.41 + '@ai-sdk/openai-compatible': ^3.0.0 '@astrojs/cloudflare': ^14.0.0 '@astrojs/netlify': ^8.0.0 '@astrojs/svelte': ^9.0.0 '@astrojs/vue': ^7.0.0 '@mixedbread/sdk': ^0.76.0 '@notionhq/client': ^2.2.15 - '@openrouter/ai-sdk-provider': ^1.5.4 + '@openrouter/ai-sdk-provider': ^3.0.0 '@oramacloud/client': ^2.1.0 - '@sanity/client': ^6.21.0 + '@sanity/client': ^6.21.0 || ^7.0.0 algoliasearch: ^5.55.0 flexsearch: ^0.8.0 typesense: ^3.0.0 @@ -1640,6 +1823,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + brace-expansion@1.1.18: + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + brace-expansion@2.1.2: resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} @@ -1660,6 +1846,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -1742,6 +1932,9 @@ packages: resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} engines: {node: '>= 18'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -1982,6 +2175,18 @@ packages: dagre-d3-es@7.0.14: resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + dayjs@1.11.21: resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} @@ -1997,6 +2202,14 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} @@ -2007,6 +2220,10 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dependency-graph@0.11.0: + resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} + engines: {node: '>= 0.6.0'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2052,8 +2269,8 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.4.12: - resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} + dompurify@3.4.14: + resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==} domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} @@ -2065,6 +2282,10 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + dset@3.1.4: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} @@ -2113,10 +2334,26 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + epub-gen-memory@1.1.2: resolution: {integrity: sha512-vwGM6MVNqKIskFzPZqhi4ZOs0ZTUXco9oDuHFX1vB2Il9pTAkaHWFBFgHrrl832dYmBPb/raGVUZXFvZYueRyw==} engines: {node: '>=10.0.0'} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-aggregate-error@1.0.14: + resolution: {integrity: sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA==} + engines: {node: '>= 0.4'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -2132,6 +2369,14 @@ packages: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} + es-toolkit@1.49.0: resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} @@ -2209,6 +2454,10 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} + expr-eval-fork@3.0.3: + resolution: {integrity: sha512-BhC+hbc5lIVjygr840n5DEkW3MQq7H9o+mc1/N7Z5uIiCFVyESLL5DIE7LNq4CYUNxy+XjA+3jRrL/h0Kt2xcg==} + engines: {node: '>=16.9.0'} + express-rate-limit@8.6.0: resolution: {integrity: sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==} engines: {node: '>= 16'} @@ -2232,6 +2481,9 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -2244,6 +2496,16 @@ packages: fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fast-xml-builder@1.3.1: + resolution: {integrity: sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==} + + fast-xml-parser@5.11.1: + resolution: {integrity: sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==} + hasBin: true + + fastdom@1.0.12: + resolution: {integrity: sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -2274,6 +2536,13 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreach@2.0.6: + resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -2290,6 +2559,17 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -2298,6 +2578,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2310,6 +2594,13 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + get-tsconfig@5.0.0-beta.4: resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} engines: {node: '>=20.20.0'} @@ -2321,6 +2612,10 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2338,10 +2633,25 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.4: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} @@ -2418,18 +2728,33 @@ packages: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + internmap@1.0.1: resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} @@ -2454,6 +2779,34 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -2462,17 +2815,45 @@ packages: engines: {node: '>=20'} hasBin: true + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-network-error@1.3.2: + resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} + engines: {node: '>=16'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} @@ -2484,13 +2865,55 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unsafe@2.0.2: + resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2516,15 +2939,22 @@ packages: resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} hasBin: true - js-yaml@4.3.0: - resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true + jsep@1.4.0: + resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} + engines: {node: '>= 10.16.0'} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true + json-pointer@0.6.2: + resolution: {integrity: sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -2542,12 +2972,20 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@2.2.1: + resolution: {integrity: sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==} + jsonc-parser@2.3.1: resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonpath-plus@10.4.0: + resolution: {integrity: sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==} + engines: {node: '>=18.0.0'} + hasBin: true + jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} @@ -2559,8 +2997,8 @@ packages: resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} hasBin: true - katex@0.17.0: - resolution: {integrity: sha512-Vdw0ATsQ9V+LuegM/BTwQqV/6cTl5lbGcIrU+BCgLxyf6bo38ybOr372tuSIxir3CN720flu1meYR6XzNMwQnw==} + katex@0.18.4: + resolution: {integrity: sha512-IMPntbRLOU+eu88XDiFKqQ8Akhr9Tv7jDMXqPhjG9SI1JMA4DIgXk4x9k4skJz2NZJXBRbC+2pYBLj9olqcZow==} hasBin: true khroma@2.1.0: @@ -2580,6 +3018,10 @@ packages: layout-base@2.0.1: resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + leven@4.1.0: resolution: {integrity: sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2668,6 +3110,12 @@ packages: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.topath@4.5.2: + resolution: {integrity: sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -2769,6 +3217,9 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + medium-zoom@1.1.0: + resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -2776,8 +3227,8 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - mermaid@11.16.0: - resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==} + mermaid@11.17.2: + resolution: {integrity: sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2905,6 +3356,9 @@ packages: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.9: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} @@ -2937,6 +3391,9 @@ packages: engines: {node: ^18 || >=20} hasBin: true + nanotar@0.3.0: + resolution: {integrity: sha512-Kv2JYYiCzt16Kt5QwAc9BFG89xfPNBx+oQL4GQXD9nLqPkZBiNaqaCWtwnbk/q7UVsTYevvM1b0UF8zmEI4pCg==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -2945,12 +3402,25 @@ packages: resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} engines: {node: '>= 10'} + nimma@0.2.3: + resolution: {integrity: sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==} + engines: {node: ^12.20 || >=14.13} + nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-fetch@2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -2964,6 +3434,9 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + node-html-parser@9.0.2: + resolution: {integrity: sha512-XhM0CTeF4Hkw3el7VCyasGJCEvp500Xf0s1J3ZigYfTOkVqZRQRY3EiAw1CbJzNrxaKSDthXiT9c/4CBwIJyfw==} + node-mock-http@1.0.4: resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} @@ -2995,6 +3468,14 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} @@ -3022,6 +3503,9 @@ packages: oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + openapi-sampler@1.7.4: + resolution: {integrity: sha512-CKS/rd5ucPCuEDbJnjGDXZTsuGWcmv53aCmQx7soZlPEONUGN4af0/dY5+THRFZraSEjeA78nlfzdFswC/N5SA==} + os-paths@4.4.0: resolution: {integrity: sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg==} engines: {node: '>= 6.0'} @@ -3030,20 +3514,32 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - p-limit@7.3.0: - resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==} + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + + p-limit@7.3.1: + resolution: {integrity: sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==} engines: {node: '>=20'} + p-map@7.0.7: + resolution: {integrity: sha512-VaWRu2i4FJNRtiRWCuuQRgfQ1B7a6+gMSrO+3j0EQi/k0ULfS9kosRxGoiqwzIjZTDI02tGfk5mXXltLg6QtfQ==} + engines: {node: '>=18'} + p-queue@9.3.1: resolution: {integrity: sha512-POWdiIPmsUPGwb4FeQ4OBg46aqmcInSWe45CKDsGHiOBiVQM9chqfQTuqhuTzcg2Vz9faTI65at0KkVyVEiCHw==} engines: {node: '>=20'} + p-retry@8.0.0: + resolution: {integrity: sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==} + engines: {node: '>=22'} + p-timeout@7.0.1: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} - package-manager-detector@1.7.0: - resolution: {integrity: sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==} + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} pagefind@1.5.2: resolution: {integrity: sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==} @@ -3071,6 +3567,10 @@ packages: path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-expression-matcher@1.6.2: + resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} + engines: {node: '>=14.0.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3088,9 +3588,15 @@ packages: path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path@0.12.7: + resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} + piccolore@0.1.3: resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} @@ -3115,6 +3621,14 @@ packages: points-on-path@0.2.1: resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + pony-cause@1.1.1: + resolution: {integrity: sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==} + engines: {node: '>=12.0.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -3139,6 +3653,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} @@ -3203,6 +3721,10 @@ packages: recma-stringify@1.0.0: resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -3212,6 +3734,10 @@ packages: regex@6.1.0: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} @@ -3273,6 +3799,10 @@ packages: retext@9.0.0: resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + robots-parser@3.0.1: + resolution: {integrity: sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==} + engines: {node: '>=10.0.0'} + robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} @@ -3291,9 +3821,24 @@ packages: rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@1.1.1: + resolution: {integrity: sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -3331,15 +3876,32 @@ packages: server-destroy@1.0.1: resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -3372,6 +3934,10 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-icons@13.21.0: resolution: {integrity: sha512-LI5pVJPBv6oc79OMsffwb6kEqnmB8P1Cjg1crNUlhsxPETQ5UzbCKQdxU+7MW6+DD1qfPkla/vSKlLD4IfyXpQ==} engines: {node: '>=0.12.18'} @@ -3405,10 +3971,33 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + strictdom@1.0.1: + resolution: {integrity: sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -3419,6 +4008,10 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom-string@1.0.0: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} @@ -3427,6 +4020,9 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + strnum@2.4.2: + resolution: {integrity: sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -3448,6 +4044,10 @@ packages: tailwindcss@4.3.3: resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + takumi-js@2.13.3: + resolution: {integrity: sha512-qjqYWO95JYYS06gJQ/eocG+KA3iLbdsauc8zlVGPwFtcMCFeBcSsIigXY+uA+AyUQKWfR67WHK6anSU8shNT9w==} + engines: {node: '>=18'} + tapable@2.3.3: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} @@ -3488,6 +4088,9 @@ packages: resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} engines: {node: '>=6.10'} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3507,6 +4110,22 @@ packages: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + typesafe-path@0.2.2: resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} @@ -3524,11 +4143,22 @@ packages: ultrahtml@1.7.0: resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - undici@8.7.0: - resolution: {integrity: sha512-N7iQtfyLhIMOFgQubvmLV26svHpO0bqKnAiWotTQCVKCmWrcGbBotPuW1x+xwYZ2VHdSTVUfPQQnlEt1/LouTQ==} + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} + engines: {node: '>=20.18.1'} + + undici@8.10.0: + resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==} engines: {node: '>=22.19.0'} unified@11.0.5: @@ -3642,9 +4272,19 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + urijs@1.19.11: + resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + uuid@14.0.1: resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true @@ -3828,6 +4468,22 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3840,6 +4496,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@8.0.0: + resolution: {integrity: sha512-dYwyZredl67GyLLIHJnRM3h2PcOmN5SkcgC7eM5DPDEOEl6dLFqVrMg3F1Ea32usj4VSVZtd2H4MtKTNOf6nPg==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + xdg-app-paths@5.5.1: resolution: {integrity: sha512-hI3flOB4PLZIy5prbtTpirobtPE2ZtZ52szO+2mM9Efp6ErM398La+C1lIpNWDfNoQk+6Lsi6nMcCwVB7pxeMQ==} engines: {node: '>= 6.0'} @@ -3848,6 +4508,10 @@ packages: resolution: {integrity: sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw==} engines: {node: '>= 6.0'} + xml-naming@0.3.0: + resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} + engines: {node: '>=16.0.0'} + xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} @@ -3897,9 +4561,6 @@ packages: peerDependencies: zod: ^3.25.28 || ^4 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.1.11: resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} @@ -3911,27 +4572,29 @@ packages: snapshots: - '@ai-sdk/gateway@2.0.115(zod@3.25.76)': + '@ai-sdk/gateway@4.0.69(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 2.0.3 - '@ai-sdk/provider-utils': 3.0.30(zod@3.25.76) - '@vercel/oidc': 3.1.0 - zod: 3.25.76 + '@ai-sdk/provider': 4.0.9 + '@ai-sdk/provider-utils': 5.0.34(zod@4.4.3) + '@vercel/oidc': 3.2.0 + zod: 4.4.3 - '@ai-sdk/provider-utils@3.0.30(zod@3.25.76)': + '@ai-sdk/provider-utils@5.0.34(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 2.0.3 + '@ai-sdk/provider': 4.0.9 '@standard-schema/spec': 1.1.0 + '@workflow/serde': 4.1.0 eventsource-parser: 3.1.0 - zod: 3.25.76 + undici: 7.29.0 + zod: 4.4.3 - '@ai-sdk/provider@2.0.3': + '@ai-sdk/provider@4.0.9': dependencies: json-schema: 0.4.0 '@antfu/install-pkg@1.1.0': dependencies: - package-manager-detector: 1.7.0 + package-manager-detector: 1.8.0 tinyexec: 1.2.4 '@astrojs/check@0.9.9(prettier@3.9.5)(typescript@6.0.3)': @@ -3963,9 +4626,9 @@ snapshots: '@astrojs/compiler-binding-linux-x64-musl@0.3.1': optional: true - '@astrojs/compiler-binding-wasm32-wasi@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@astrojs/compiler-binding-wasm32-wasi@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': dependencies: - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3977,7 +4640,7 @@ snapshots: '@astrojs/compiler-binding-win32-x64-msvc@0.3.1': optional: true - '@astrojs/compiler-binding@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@astrojs/compiler-binding@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': optionalDependencies: '@astrojs/compiler-binding-darwin-arm64': 0.3.1 '@astrojs/compiler-binding-darwin-x64': 0.3.1 @@ -3985,16 +4648,16 @@ snapshots: '@astrojs/compiler-binding-linux-arm64-musl': 0.3.1 '@astrojs/compiler-binding-linux-x64-gnu': 0.3.1 '@astrojs/compiler-binding-linux-x64-musl': 0.3.1 - '@astrojs/compiler-binding-wasm32-wasi': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@astrojs/compiler-binding-wasm32-wasi': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) '@astrojs/compiler-binding-win32-arm64-msvc': 0.3.1 '@astrojs/compiler-binding-win32-x64-msvc': 0.3.1 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - '@astrojs/compiler-rs@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@astrojs/compiler-rs@0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': dependencies: - '@astrojs/compiler-binding': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@astrojs/compiler-binding': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -4005,7 +4668,7 @@ snapshots: dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 - js-yaml: 4.3.0 + js-yaml: 4.3.2 picomatch: 4.0.5 retext-smartypants: 6.2.0 shiki: 4.3.1 @@ -4067,13 +4730,13 @@ snapshots: hast-util-from-html: 2.0.3 satteri: 0.9.5 - '@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))': + '@astrojs/mdx@7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@astrojs/internal-helpers': 0.10.1 '@astrojs/markdown-remark': 7.2.1 '@mdx-js/mdx': 3.1.1 acorn: 8.17.0 - astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) + astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) es-module-lexer: 2.3.1 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -4089,10 +4752,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/node@11.0.2(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))': + '@astrojs/node@11.0.2(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@astrojs/internal-helpers': 0.10.1 - astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) + astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) send: 1.2.1 server-destroy: 1.0.1 transitivePeerDependencies: @@ -4102,17 +4765,17 @@ snapshots: dependencies: prismjs: 1.30.0 - '@astrojs/react@6.0.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(yaml@2.9.0)': + '@astrojs/react@6.0.1(@types/node@26.4.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(yaml@2.9.0)': dependencies: '@astrojs/internal-helpers': 0.10.1 '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@vitejs/plugin-react': 5.2.0(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@vitejs/plugin-react': 5.2.0(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) devalue: 5.8.1 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) ultrahtml: 1.7.0 - vite: 8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - '@vitejs/devtools' @@ -4133,16 +4796,16 @@ snapshots: ci-info: 4.4.0 dset: 3.1.4 is-docker: 4.0.0 - package-manager-detector: 1.7.0 + package-manager-detector: 1.8.0 - '@astrojs/vercel@11.0.3(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))(react@19.2.7)': + '@astrojs/vercel@11.0.3(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))(react@19.2.7)': dependencies: '@astrojs/internal-helpers': 0.10.1 '@vercel/analytics': 1.6.1(react@19.2.7) '@vercel/functions': 3.7.5 '@vercel/nft': 1.10.2 '@vercel/routing-utils': 5.3.3 - astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) + astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) esbuild: 0.28.1 tinyglobby: 0.2.17 transitivePeerDependencies: @@ -4163,6 +4826,42 @@ snapshots: dependencies: yaml: 2.9.0 + '@asyncapi/converter@2.0.2': + dependencies: + '@asyncapi/parser': 3.6.3 + js-yaml: 3.15.0 + path: 0.12.7 + transitivePeerDependencies: + - encoding + + '@asyncapi/parser@3.6.3': + dependencies: + '@asyncapi/specs': 6.11.1 + '@openapi-contrib/openapi-schema-to-json-schema': 3.2.0 + '@stoplight/json': 3.21.0 + '@stoplight/json-ref-readers': 1.2.2 + '@stoplight/json-ref-resolver': 3.1.6 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-functions': 1.10.5 + '@stoplight/spectral-parsers': 1.0.5 + '@stoplight/spectral-ref-resolver': 1.0.5 + '@stoplight/types': 13.20.0 + '@types/json-schema': 7.0.15 + '@types/urijs': 1.19.26 + ajv: 8.20.0 + ajv-errors: 3.0.0(ajv@8.20.0) + ajv-formats: 2.1.1(ajv@8.20.0) + avsc: 5.7.9 + js-yaml: 4.3.2 + jsonpath-plus: 10.4.0 + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + + '@asyncapi/specs@6.11.1': + dependencies: + '@types/json-schema': 7.0.15 + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -4360,6 +5059,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.2': dependencies: tslib: 2.8.1 @@ -4459,101 +5163,110 @@ snapshots: '@iconify/types': 2.0.0 import-meta-resolve: 4.2.0 - '@img/colour@1.1.0': - optional: true + '@img/colour@1.1.0': {} - '@img/sharp-darwin-arm64@0.34.5': + '@img/sharp-darwin-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 optional: true - '@img/sharp-darwin-x64@0.34.5': + '@img/sharp-darwin-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.3.3 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.4': + dependencies: + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + '@img/sharp-libvips-darwin-arm64@1.3.3': optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + '@img/sharp-libvips-darwin-x64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + '@img/sharp-libvips-linux-arm64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + '@img/sharp-libvips-linux-arm@1.3.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + '@img/sharp-libvips-linux-ppc64@1.3.3': optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + '@img/sharp-libvips-linux-riscv64@1.3.3': optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + '@img/sharp-libvips-linux-s390x@1.3.3': optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + '@img/sharp-libvips-linux-x64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + '@img/sharp-libvips-linuxmusl-x64@1.3.3': optional: true - '@img/sharp-linux-arm64@0.34.5': + '@img/sharp-linux-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.3.3 optional: true - '@img/sharp-linux-arm@0.34.5': + '@img/sharp-linux-arm@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.3.3 optional: true - '@img/sharp-linux-ppc64@0.34.5': + '@img/sharp-linux-ppc64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.3.3 optional: true - '@img/sharp-linux-riscv64@0.34.5': + '@img/sharp-linux-riscv64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.3.3 optional: true - '@img/sharp-linux-s390x@0.34.5': + '@img/sharp-linux-s390x@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.3.3 optional: true - '@img/sharp-linux-x64@0.34.5': + '@img/sharp-linux-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.3.3 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + '@img/sharp-linuxmusl-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + '@img/sharp-linuxmusl-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 optional: true - '@img/sharp-wasm32@0.34.5': + '@img/sharp-wasm32@0.35.4': dependencies: - '@emnapi/runtime': 1.11.1 + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.4': + dependencies: + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-win32-arm64@0.34.5': + '@img/sharp-win32-arm64@0.35.4': optional: true - '@img/sharp-win32-ia32@0.34.5': + '@img/sharp-win32-ia32@0.35.4': optional: true - '@img/sharp-win32-x64@0.34.5': + '@img/sharp-win32-x64@0.35.4': optional: true '@isaacs/fs-minipass@4.0.1': @@ -4579,6 +5292,18 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsep-plugin/ternary@1.1.4(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + '@mapbox/node-pre-gyp@2.0.3': dependencies: consola: 3.4.2 @@ -4622,11 +5347,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@mermaid-js/parser@1.2.0': + '@mermaid-js/parser@1.2.1': dependencies: '@chevrotain/types': 11.1.2 - '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.30) ajv: 8.20.0 @@ -4643,8 +5368,8 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - supports-color @@ -4655,7 +5380,18 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@opentelemetry/api@1.9.0': {} + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@nodable/entities@3.0.0': {} + + '@openapi-contrib/openapi-schema-to-json-schema@3.2.0': + dependencies: + fast-deep-equal: 3.1.3 '@orama/orama@3.1.18': {} @@ -4767,10 +5503,10 @@ snapshots: estree-walker: 2.0.2 picomatch: 4.0.5 - '@scalar/astro@0.4.11(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))': + '@scalar/astro@0.4.11(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@scalar/client-side-rendering': 0.3.4 - astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) + astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) '@scalar/client-side-rendering@0.3.4': dependencies: @@ -4819,6 +5555,8 @@ snapshots: '@scalar/validation@0.6.2': {} + '@scarf/scarf@1.4.0': {} + '@shikijs/core@4.3.1': dependencies: '@shikijs/primitive': 4.3.1 @@ -4877,6 +5615,151 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@stoplight/better-ajv-errors@1.0.3(ajv@8.20.0)': + dependencies: + ajv: 8.20.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@stoplight/json-ref-readers@1.2.2': + dependencies: + node-fetch: 2.7.0 + tslib: 1.14.1 + transitivePeerDependencies: + - encoding + + '@stoplight/json-ref-resolver@3.1.6': + dependencies: + '@stoplight/json': 3.21.0 + '@stoplight/path': 1.3.2 + '@stoplight/types': 13.20.0 + '@types/urijs': 1.19.26 + dependency-graph: 0.11.0 + fast-memoize: 2.5.2 + immer: 9.0.21 + lodash: 4.18.1 + tslib: 2.8.1 + urijs: 1.19.11 + + '@stoplight/json@3.21.0': + dependencies: + '@stoplight/ordered-object-literal': 1.0.5 + '@stoplight/path': 1.3.2 + '@stoplight/types': 13.20.0 + jsonc-parser: 2.2.1 + lodash: 4.18.1 + safe-stable-stringify: 1.1.1 + + '@stoplight/ordered-object-literal@1.0.5': {} + + '@stoplight/path@1.3.2': {} + + '@stoplight/spectral-core@1.23.1': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/better-ajv-errors': 1.0.3(ajv@8.20.0) + '@stoplight/json': 3.21.0 + '@stoplight/path': 1.3.2 + '@stoplight/spectral-parsers': 1.0.5 + '@stoplight/spectral-ref-resolver': 1.0.5 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.6.0 + '@types/es-aggregate-error': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-errors: 3.0.0(ajv@8.20.0) + ajv-formats: 2.1.1(ajv@8.20.0) + es-aggregate-error: 1.0.14 + expr-eval-fork: 3.0.3 + jsonpath-plus: 10.4.0 + lodash: 4.18.1 + lodash.topath: 4.5.2 + minimatch: 3.1.5 + nimma: 0.2.3 + pony-cause: 1.1.1 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-formats@1.8.5': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/json': 3.21.0 + '@stoplight/spectral-core': 1.23.1 + '@types/json-schema': 7.0.15 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-functions@1.10.5': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/better-ajv-errors': 1.0.3(ajv@8.20.0) + '@stoplight/json': 3.21.0 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-formats': 1.8.5 + '@stoplight/spectral-runtime': 1.1.6 + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-errors: 3.0.0(ajv@8.20.0) + ajv-formats: 2.1.1(ajv@8.20.0) + lodash: 4.18.1 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-parsers@1.0.5': + dependencies: + '@stoplight/json': 3.21.0 + '@stoplight/types': 14.1.1 + '@stoplight/yaml': 4.3.0 + tslib: 2.8.1 + + '@stoplight/spectral-ref-resolver@1.0.5': + dependencies: + '@stoplight/json-ref-readers': 1.2.2 + '@stoplight/json-ref-resolver': 3.1.6 + '@stoplight/spectral-runtime': 1.1.6 + dependency-graph: 0.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-runtime@1.1.6': + dependencies: + '@stoplight/json': 3.21.0 + '@stoplight/path': 1.3.2 + '@stoplight/types': 13.20.0 + lodash: 4.18.1 + node-fetch: 2.7.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/types@13.20.0': + dependencies: + '@types/json-schema': 7.0.15 + utility-types: 3.11.0 + + '@stoplight/types@13.6.0': + dependencies: + '@types/json-schema': 7.0.15 + utility-types: 3.11.0 + + '@stoplight/types@14.1.1': + dependencies: + '@types/json-schema': 7.0.15 + utility-types: 3.11.0 + + '@stoplight/yaml-ast-parser@0.0.50': {} + + '@stoplight/yaml@4.3.0': + dependencies: + '@stoplight/ordered-object-literal': 1.0.5 + '@stoplight/types': 14.1.1 + '@stoplight/yaml-ast-parser': 0.0.50 + tslib: 2.8.1 + '@tailwindcss/node@4.3.3': dependencies: '@jridgewell/remapping': 2.3.5 @@ -4943,57 +5826,66 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.3.3 - '@tailwindcss/vite@4.3.3(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.3(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: 8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) - '@takumi-rs/core-darwin-arm64@1.8.7': + '@takumi-rs/core-darwin-arm64@2.13.3': optional: true - '@takumi-rs/core-darwin-x64@1.8.7': + '@takumi-rs/core-darwin-x64@2.13.3': optional: true - '@takumi-rs/core-linux-arm64-gnu@1.8.7': + '@takumi-rs/core-linux-arm64-gnu@2.13.3': optional: true - '@takumi-rs/core-linux-arm64-musl@1.8.7': + '@takumi-rs/core-linux-arm64-musl@2.13.3': optional: true - '@takumi-rs/core-linux-x64-gnu@1.8.7': + '@takumi-rs/core-linux-x64-gnu@2.13.3': optional: true - '@takumi-rs/core-linux-x64-musl@1.8.7': + '@takumi-rs/core-linux-x64-musl@2.13.3': optional: true - '@takumi-rs/core-win32-arm64-msvc@1.8.7': + '@takumi-rs/core-win32-arm64-msvc@2.13.3': optional: true - '@takumi-rs/core-win32-x64-msvc@1.8.7': + '@takumi-rs/core-win32-x64-msvc@2.13.3': optional: true - '@takumi-rs/core@1.8.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@takumi-rs/core@2.13.3(csstype@3.2.3)(react@19.2.7)': dependencies: - '@takumi-rs/helpers': 1.8.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@takumi-rs/helpers': 2.13.3(react@19.2.7) optionalDependencies: - '@takumi-rs/core-darwin-arm64': 1.8.7 - '@takumi-rs/core-darwin-x64': 1.8.7 - '@takumi-rs/core-linux-arm64-gnu': 1.8.7 - '@takumi-rs/core-linux-arm64-musl': 1.8.7 - '@takumi-rs/core-linux-x64-gnu': 1.8.7 - '@takumi-rs/core-linux-x64-musl': 1.8.7 - '@takumi-rs/core-win32-arm64-msvc': 1.8.7 - '@takumi-rs/core-win32-x64-msvc': 1.8.7 + '@takumi-rs/core-darwin-arm64': 2.13.3 + '@takumi-rs/core-darwin-x64': 2.13.3 + '@takumi-rs/core-linux-arm64-gnu': 2.13.3 + '@takumi-rs/core-linux-arm64-musl': 2.13.3 + '@takumi-rs/core-linux-x64-gnu': 2.13.3 + '@takumi-rs/core-linux-x64-musl': 2.13.3 + '@takumi-rs/core-win32-arm64-msvc': 2.13.3 + '@takumi-rs/core-win32-x64-msvc': 2.13.3 + csstype: 3.2.3 transitivePeerDependencies: + - preact - react - - react-dom - '@takumi-rs/helpers@1.8.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@takumi-rs/helpers@2.13.3(react@19.2.7)': optionalDependencies: react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + + '@takumi-rs/wasm@2.13.3(csstype@3.2.3)(react@19.2.7)': + dependencies: + '@takumi-rs/helpers': 2.13.3(react@19.2.7) + optionalDependencies: + csstype: 3.2.3 + transitivePeerDependencies: + - preact + - react '@tybys/wasm-util@0.10.3': dependencies: @@ -5142,6 +6034,10 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/es-aggregate-error@1.0.6': + dependencies: + '@types/node': 26.4.0 + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.9 @@ -5154,6 +6050,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -5166,6 +6064,10 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/node@26.4.0': + dependencies: + undici-types: 8.3.0 + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: '@types/react': 19.2.17 @@ -5181,6 +6083,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/urijs@1.19.26': {} + '@typescript/vfs@1.6.4(typescript@6.0.3)': dependencies: debug: 4.4.3 @@ -5235,7 +6139,7 @@ snapshots: - rollup - supports-color - '@vercel/oidc@3.1.0': {} + '@vercel/oidc@3.2.0': {} '@vercel/oidc@3.8.0': dependencies: @@ -5250,7 +6154,7 @@ snapshots: optionalDependencies: ajv: 6.15.0 - '@vitejs/plugin-react@5.2.0(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) @@ -5258,7 +6162,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -5312,6 +6216,8 @@ snapshots: '@vscode/l10n@0.0.18': {} + '@workflow/serde@4.1.0': {} + abbrev@3.0.1: {} abort-controller@3.0.0: @@ -5335,18 +6241,25 @@ snapshots: agent-base@7.1.4: {} - ai@5.0.216(zod@3.25.76): + ai@7.0.85(zod@4.4.3): dependencies: - '@ai-sdk/gateway': 2.0.115(zod@3.25.76) - '@ai-sdk/provider': 2.0.3 - '@ai-sdk/provider-utils': 3.0.30(zod@3.25.76) - '@opentelemetry/api': 1.9.0 - zod: 3.25.76 + '@ai-sdk/gateway': 4.0.69(zod@4.4.3) + '@ai-sdk/provider': 4.0.9 + '@ai-sdk/provider-utils': 5.0.34(zod@4.4.3) + zod: 4.4.3 ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 + ajv-errors@3.0.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + ajv-formats@3.0.1(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -5376,6 +6289,8 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.3.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -5385,6 +6300,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + anynum@1.0.1: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -5393,13 +6310,28 @@ snapshots: aria-query@5.3.2: {} + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + array-iterate@2.0.1: {} + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + astring@1.9.0: {} - astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0): + astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0): dependencies: - '@astrojs/compiler-rs': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@astrojs/compiler-rs': 0.3.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) '@astrojs/internal-helpers': 0.10.1 '@astrojs/markdown-satteri': 0.3.4 '@astrojs/telemetry': 3.3.3 @@ -5425,16 +6357,16 @@ snapshots: github-slugger: 2.0.0 html-escaper: 3.0.3 http-cache-semantics: 4.2.0 - js-yaml: 4.3.0 + js-yaml: 4.3.2 jsonc-parser: 3.3.1 magic-string: 0.30.21 magicast: 0.5.3 mrmime: 2.0.1 neotraverse: 0.6.18 obug: 2.1.4 - p-limit: 7.3.0 + p-limit: 7.3.1 p-queue: 9.3.1 - package-manager-detector: 1.7.0 + package-manager-detector: 1.8.0 piccolore: 0.1.3 picomatch: 4.0.5 semver: 7.8.5 @@ -5447,14 +6379,14 @@ snapshots: ultrahtml: 1.7.0 unifont: 0.7.4 unstorage: 1.17.5(@vercel/functions@3.7.5) - vite: 8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + vite: 8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 optionalDependencies: '@astrojs/markdown-remark': 7.2.1 - sharp: 0.34.5 + sharp: 0.35.4(@types/node@26.4.0) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -5490,10 +6422,18 @@ snapshots: - uploadthing - yaml + async-function@1.0.0: {} + async-sema@3.1.1: {} async@3.2.6: {} + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + avsc@5.7.9: {} + axobject-query@4.1.0: {} babel-plugin-react-compiler@1.0.0: @@ -5512,57 +6452,87 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 - blume@1.0.4(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@shikijs/themes@4.3.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@vercel/functions@3.7.5)(esbuild@0.28.1)(prettier@3.9.5)(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(yaml@2.9.0): + blume@1.5.3(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@shikijs/themes@4.3.1)(@types/node@26.4.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(@vercel/functions@3.7.5)(csstype@3.2.3)(esbuild@0.28.1)(prettier@3.9.5)(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(yaml@2.9.0): dependencies: '@astrojs/check': 0.9.9(prettier@3.9.5)(typescript@6.0.3) '@astrojs/markdown-satteri': 0.3.4 - '@astrojs/mdx': 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0)) - '@astrojs/node': 11.0.2(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0)) - '@astrojs/react': 6.0.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(yaml@2.9.0) - '@astrojs/vercel': 11.0.3(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))(react@19.2.7) + '@astrojs/mdx': 7.0.3(@astrojs/markdown-satteri@0.3.4)(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0)) + '@astrojs/node': 11.0.2(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0)) + '@astrojs/react': 6.0.1(@types/node@26.4.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(jiti@2.7.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(yaml@2.9.0) + '@astrojs/vercel': 11.0.3(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0))(react@19.2.7) + '@asyncapi/converter': 2.0.2 '@clack/prompts': 1.7.0 '@iconify-json/lucide': 1.2.118 '@iconify/types': 2.0.0 '@iconify/utils': 3.1.4 - '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) '@orama/orama': 3.1.18 '@pierre/diffs': 1.2.12(@shikijs/themes@4.3.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@scalar/astro': 0.4.11(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0)) + '@scalar/astro': 0.4.11(astro@7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0)) '@scalar/openapi-parser': 0.28.10 '@scalar/openapi-types': 0.9.3 '@shikijs/transformers': 4.3.1 '@shikijs/twoslash': 4.3.1(typescript@6.0.3) '@tailwindcss/typography': 0.5.20(tailwindcss@4.3.3) - '@tailwindcss/vite': 4.3.3(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) - '@takumi-rs/core': 1.8.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@takumi-rs/helpers': 1.8.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tailwindcss/vite': 4.3.3(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + '@types/mdast': 4.0.4 '@vercel/analytics': 2.0.1(react@19.2.7) - ai: 5.0.216(zod@3.25.76) - astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) + ai: 7.0.85(zod@4.4.3) + astro: 7.1.1(@astrojs/markdown-remark@7.2.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@26.4.0)(@vercel/functions@3.7.5)(jiti@2.7.0)(yaml@2.9.0) babel-plugin-react-compiler: 1.0.0 + chokidar: 5.0.0 citty: 0.1.6 consola: 3.4.2 - dompurify: 3.4.12 + cross-spawn: 7.0.6 + dompurify: 3.4.14 + dotenv: 17.4.2 epub-gen-memory: 1.1.2 + fast-xml-parser: 5.11.1 + get-tsconfig: 4.14.3 github-slugger: 2.0.0 gray-matter: 4.0.3 + html-escaper: 3.0.3 + image-size: 2.0.2 jiti: 2.7.0 - js-yaml: 4.3.0 - katex: 0.17.0 + js-yaml: 4.3.2 + katex: 0.18.4 + markdown-table: 3.0.4 marked: 18.0.6 - mermaid: 11.16.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm: 3.1.0 + mdast-util-to-string: 4.0.0 + medium-zoom: 1.1.0 + mermaid: 11.17.2 + micromark-extension-gfm: 3.0.0 + nanotar: 0.3.0 + node-html-parser: 9.0.2 + openapi-sampler: 1.7.4 + p-limit: 7.3.1 + p-map: 7.0.7 + p-retry: 8.0.0 + package-manager-detector: 1.8.0 pagefind: 1.5.2 pathe: 2.0.3 + perfect-debounce: 2.1.0 + picomatch: 4.0.5 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + robots-parser: 3.0.1 satteri: 0.9.5 + semver: 7.8.5 + sharp: 0.35.4(@types/node@26.4.0) shiki: 4.3.1 simple-icons: 13.21.0 + string-width: 8.2.2 tailwindcss: 4.3.3 + takumi-js: 2.13.3(csstype@3.2.3)(react@19.2.7) tinyglobby: 0.2.17 + twoslash: 0.3.9(typescript@6.0.3) typescript: 6.0.3 - undici: 8.7.0 - zod: 3.25.76 + ufo: 1.6.4 + undici: 8.10.0 + write-file-atomic: 8.0.0 + zod: 4.4.3 transitivePeerDependencies: - '@astrojs/markdown-remark' - '@aws-sdk/credential-provider-web-identity' @@ -5591,6 +6561,7 @@ snapshots: - '@vercel/kv' - '@vitejs/devtools' - aws4fetch + - csstype - db0 - encoding - esbuild @@ -5599,6 +6570,7 @@ snapshots: - less - next - nuxt + - preact - prettier - prettier-plugin-astro - rollup @@ -5633,6 +6605,11 @@ snapshots: boolbase@1.0.0: {} + brace-expansion@1.1.18: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.1.2: dependencies: balanced-match: 1.0.2 @@ -5656,6 +6633,13 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5717,6 +6701,8 @@ snapshots: common-ancestor-path@2.0.0: {} + concat-map@0.0.1: {} + consola@3.4.2: {} content-disposition@1.1.0: {} @@ -5980,6 +6966,24 @@ snapshots: d3: 7.9.0 lodash-es: 4.18.1 + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + dayjs@1.11.21: {} debug@4.4.3: @@ -5990,6 +6994,18 @@ snapshots: dependencies: character-entities: 2.0.2 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + defu@6.1.7: {} delaunator@5.1.0: @@ -5998,6 +7014,8 @@ snapshots: depd@2.0.0: {} + dependency-graph@0.11.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -6038,7 +7056,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.4.12: + dompurify@3.4.14: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -6058,6 +7076,8 @@ snapshots: dependencies: is-obj: 2.0.0 + dotenv@17.4.2: {} + dset@3.1.4: {} dunder-proto@1.0.1: @@ -6096,6 +7116,8 @@ snapshots: entities@6.0.1: {} + entities@8.0.0: {} + epub-gen-memory@1.1.2: dependencies: abort-controller: 3.0.0 @@ -6114,25 +7136,116 @@ snapshots: transitivePeerDependencies: - encoding - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@2.3.1: {} - - es-object-atoms@1.1.2: + es-abstract-get@1.0.0: dependencies: es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 - es-toolkit@1.49.0: {} - - esast-util-from-estree@2.0.0: + es-abstract@1.24.2: dependencies: - '@types/estree-jsx': 1.0.5 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - unist-util-position-from-estree: 2.0.0 - + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.2 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-aggregate-error@1.0.14: + dependencies: + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + function-bind: 1.1.2 + globalthis: 1.0.4 + has-property-descriptors: 1.0.2 + set-function-name: 2.0.2 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.1: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es-toolkit@1.49.0: {} + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + esast-util-from-js@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 @@ -6236,6 +7349,8 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + expr-eval-fork@3.0.3: {} + express-rate-limit@8.6.0(express@5.2.1): dependencies: debug: 4.4.3 @@ -6288,6 +7403,8 @@ snapshots: fast-json-stable-stringify@2.1.0: optional: true + fast-memoize@2.5.2: {} + fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: @@ -6300,6 +7417,24 @@ snapshots: dependencies: fast-string-width: 3.0.2 + fast-xml-builder@1.3.1: + dependencies: + path-expression-matcher: 1.6.2 + xml-naming: 0.3.0 + + fast-xml-parser@5.11.1: + dependencies: + '@nodable/entities': 3.0.0 + fast-xml-builder: 1.3.1 + is-unsafe: 2.0.2 + path-expression-matcher: 1.6.2 + strnum: 2.4.2 + xml-naming: 0.3.0 + + fastdom@1.0.12: + dependencies: + strictdom: 1.0.1 + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 @@ -6331,6 +7466,12 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreach@2.0.6: {} + forwarded@0.2.0: {} fresh@2.0.0: {} @@ -6340,10 +7481,28 @@ snapshots: function-bind@1.1.2: {} + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} + get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6364,6 +7523,16 @@ snapshots: get-stream@6.0.1: {} + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.3: + dependencies: + resolve-pkg-maps: 1.0.0 + get-tsconfig@5.0.0-beta.4: dependencies: resolve-pkg-maps: 1.0.0 @@ -6376,6 +7545,11 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -6401,8 +7575,22 @@ snapshots: hachure-fill@0.5.2: {} + has-bigints@1.1.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -6575,14 +7763,26 @@ snapshots: dependencies: safer-buffer: 2.1.2 + image-size@2.0.2: {} + immediate@3.0.6: {} + immer@9.0.21: {} + import-meta-resolve@4.2.0: {} + inherits@2.0.3: {} + inherits@2.0.4: {} inline-style-parser@0.2.7: {} + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + internmap@1.0.1: {} internmap@2.0.3: {} @@ -6600,26 +7800,132 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} is-docker@4.0.0: {} + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + is-extendable@0.1.1: {} + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + is-hexadecimal@2.0.1: {} + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-network-error@1.3.2: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-obj@2.0.0: {} is-plain-obj@4.1.0: {} is-promise@4.0.0: {} + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + is-stream@2.0.1: {} + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-unsafe@2.0.2: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + isarray@1.0.0: {} + isarray@2.0.5: {} + isexe@2.0.0: {} jake@10.9.4: @@ -6641,12 +7947,18 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.3.0: + js-yaml@4.3.2: dependencies: argparse: 2.0.1 + jsep@1.4.0: {} + jsesc@3.1.0: {} + json-pointer@0.6.2: + dependencies: + foreach: 2.0.6 + json-schema-traverse@0.4.1: optional: true @@ -6658,10 +7970,18 @@ snapshots: json5@2.2.3: {} + jsonc-parser@2.2.1: {} + jsonc-parser@2.3.1: {} jsonc-parser@3.3.1: {} + jsonpath-plus@10.4.0: + dependencies: + '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) + '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) + jsep: 1.4.0 + jsonpointer@5.0.1: {} jszip@3.10.1: @@ -6675,7 +7995,7 @@ snapshots: dependencies: commander: 8.3.0 - katex@0.17.0: + katex@0.18.4: dependencies: commander: 8.3.0 @@ -6689,6 +8009,8 @@ snapshots: layout-base@2.0.1: {} + leven@3.1.0: {} + leven@4.1.0: {} lie@3.3.0: @@ -6748,6 +8070,10 @@ snapshots: lodash.isequal@4.5.0: {} + lodash.topath@4.5.2: {} + + lodash@4.18.1: {} + longest-streak@3.1.0: {} lru-cache@11.5.2: {} @@ -6953,15 +8279,17 @@ snapshots: media-typer@1.1.0: {} + medium-zoom@1.1.0: {} + merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} - mermaid@11.16.0: + mermaid@11.17.2: dependencies: '@braintree/sanitize-url': 7.1.2 '@iconify/utils': 3.1.4 - '@mermaid-js/parser': 1.2.0 + '@mermaid-js/parser': 1.2.1 '@types/d3': 7.4.3 '@upsetjs/venn.js': 2.0.0 cytoscape: 3.34.0 @@ -6971,8 +8299,9 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.14 dayjs: 1.11.21 - dompurify: 3.4.12 + dompurify: 3.4.14 es-toolkit: 1.49.0 + fastdom: 1.0.12 katex: 0.16.47 khroma: 2.1.0 marked: 16.4.2 @@ -7259,6 +8588,10 @@ snapshots: dependencies: brace-expansion: 5.0.7 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.18 + minimatch@5.1.9: dependencies: brace-expansion: 2.1.2 @@ -7279,22 +8612,43 @@ snapshots: nanoid@5.1.16: {} + nanotar@0.3.0: {} + negotiator@1.0.0: {} neotraverse@0.6.18: {} + nimma@0.2.3: + dependencies: + '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) + '@jsep-plugin/ternary': 1.1.4(jsep@1.4.0) + astring: 1.9.0 + jsep: 1.4.0 + optionalDependencies: + jsonpath-plus: 10.4.0 + lodash.topath: 4.5.2 + nlcst-to-string@4.0.0: dependencies: '@types/nlcst': 2.0.3 node-fetch-native@1.6.7: {} + node-fetch@2.6.7: + dependencies: + whatwg-url: 5.0.0 + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 node-gyp-build@4.8.4: {} + node-html-parser@9.0.2: + dependencies: + css-select: 5.2.2 + entities: 8.0.0 + node-mock-http@1.0.4: {} node-releases@2.0.51: {} @@ -7317,6 +8671,17 @@ snapshots: object-inspect@1.13.4: {} + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + obug@2.1.4: {} ofetch@1.5.1: @@ -7347,6 +8712,12 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 + openapi-sampler@1.7.4: + dependencies: + '@types/json-schema': 7.0.15 + fast-xml-parser: 5.11.1 + json-pointer: 0.6.2 + os-paths@4.4.0: {} ow@0.28.2: @@ -7357,18 +8728,31 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - p-limit@7.3.0: + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@7.3.1: dependencies: yocto-queue: 1.2.2 + p-map@7.0.7: {} + p-queue@9.3.1: dependencies: eventemitter3: 5.0.4 p-timeout: 7.0.1 + p-retry@8.0.0: + dependencies: + is-network-error: 1.3.2 + p-timeout@7.0.1: {} - package-manager-detector@1.7.0: {} + package-manager-detector@1.8.0: {} pagefind@1.5.2: optionalDependencies: @@ -7411,6 +8795,8 @@ snapshots: path-data-parser@0.1.0: {} + path-expression-matcher@1.6.2: {} + path-key@3.1.1: {} path-scurry@2.0.2: @@ -7424,8 +8810,15 @@ snapshots: path-to-regexp@8.4.2: {} + path@0.12.7: + dependencies: + process: 0.11.10 + util: 0.10.4 + pathe@2.0.3: {} + perfect-debounce@2.1.0: {} + piccolore@0.1.3: {} picocolors@1.1.1: {} @@ -7443,6 +8836,10 @@ snapshots: path-data-parser: 0.1.0 points-on-curve: 0.2.0 + pony-cause@1.1.1: {} + + possible-typed-array-names@1.1.0: {} + postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 @@ -7462,6 +8859,8 @@ snapshots: process-nextick-args@2.0.1: {} + process@0.11.10: {} + property-information@7.2.0: {} proxy-addr@2.0.7: @@ -7540,6 +8939,17 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -7550,6 +8960,15 @@ snapshots: dependencies: regex-utilities: 2.3.0 + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.5 @@ -7655,6 +9074,8 @@ snapshots: retext-stringify: 4.0.0 unified: 11.0.5 + robots-parser@3.0.1: {} + robust-predicates@3.0.3: {} rolldown@1.1.5: @@ -7697,8 +9118,29 @@ snapshots: rw@1.3.3: {} + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@1.1.1: {} + safer-buffer@2.1.2: {} satteri@0.9.5: @@ -7758,41 +9200,64 @@ snapshots: server-destroy@1.0.1: {} + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + setimmediate@1.0.5: {} setprototypeof@1.2.0: {} - sharp@0.34.5: + sharp@0.35.4(@types/node@26.4.0): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - optional: true + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 + '@types/node': 26.4.0 shebang-command@2.0.0: dependencies: @@ -7841,6 +9306,8 @@ snapshots: signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + simple-icons@13.21.0: {} sisteransi@1.0.5: {} @@ -7859,12 +9326,48 @@ snapshots: statuses@2.0.2: {} + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + strictdom@1.0.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -7878,10 +9381,18 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.3.0 + strip-bom-string@1.0.0: {} strip-final-newline@2.0.0: {} + strnum@2.4.2: + dependencies: + anynum: 1.0.1 + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -7906,6 +9417,16 @@ snapshots: tailwindcss@4.3.3: {} + takumi-js@2.13.3(csstype@3.2.3)(react@19.2.7): + dependencies: + '@takumi-rs/core': 2.13.3(csstype@3.2.3)(react@19.2.7) + '@takumi-rs/helpers': 2.13.3(react@19.2.7) + '@takumi-rs/wasm': 2.13.3(csstype@3.2.3)(react@19.2.7) + transitivePeerDependencies: + - csstype + - preact + - react + tapable@2.3.3: {} tar@7.5.20: @@ -7937,8 +9458,9 @@ snapshots: ts-dedent@2.3.0: {} - tslib@2.8.1: - optional: true + tslib@1.14.1: {} + + tslib@2.8.1: {} twoslash-protocol@0.3.9: {} @@ -7960,6 +9482,39 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + typesafe-path@0.2.2: {} typescript-auto-import-cache@0.3.6: @@ -7972,9 +9527,20 @@ snapshots: ultrahtml@1.7.0: {} + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + uncrypto@0.1.3: {} - undici@8.7.0: {} + undici-types@8.3.0: {} + + undici@7.29.0: {} + + undici@8.10.0: {} unified@11.0.5: dependencies: @@ -8064,8 +9630,16 @@ snapshots: punycode: 2.3.1 optional: true + urijs@1.19.11: {} + util-deprecate@1.0.2: {} + util@0.10.4: + dependencies: + inherits: 2.0.3 + + utility-types@3.11.0: {} + uuid@14.0.1: {} vali-date@1.0.0: {} @@ -8087,7 +9661,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0): + vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -8095,14 +9669,15 @@ snapshots: rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 26.4.0 esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 yaml: 2.9.0 - vitefu@1.1.3(vite@8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): optionalDependencies: - vite: 8.1.5(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) + vite: 8.1.5(@types/node@26.4.0)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) volar-service-css@0.0.71(@volar/language-service@2.4.28): dependencies: @@ -8219,6 +9794,47 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -8231,6 +9847,10 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@8.0.0: + dependencies: + signal-exit: 4.1.0 + xdg-app-paths@5.5.1: dependencies: os-paths: 4.4.0 @@ -8240,6 +9860,8 @@ snapshots: dependencies: os-paths: 4.4.0 + xml-naming@0.3.0: {} + xxhash-wasm@1.1.0: {} y18n@5.0.8: {} @@ -8283,11 +9905,9 @@ snapshots: yocto-queue@1.2.2: {} - zod-to-json-schema@3.25.2(zod@3.25.76): + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: - zod: 3.25.76 - - zod@3.25.76: {} + zod: 4.4.3 zod@4.1.11: {} diff --git a/docs/architecture/data-model.md b/docs/architecture/data-model.md index ff203628..f02c74af 100644 --- a/docs/architecture/data-model.md +++ b/docs/architecture/data-model.md @@ -65,8 +65,10 @@ repairs run as idempotent migrations guarded by feature flags. The groups: ## What is not persisted -- **LLM API keys** — stored in user settings via Tauri preferences, not in - SQLite review tables. +- **Direct LLM API keys** — not persisted by the active review/standards +configuration. Legacy provider fields are allowlist-scrubbed from localStorage +on the next read; installed agent CLI credentials remain external to +CodeVetter. - **Raw CLI agent transcripts** — read from disk on demand; only parsed summaries land in SQLite. - **Structural graph for unopened repos** — built on demand and persisted per diff --git a/docs/development/performance.md b/docs/development/performance.md index 350219ad..caf2acd1 100644 --- a/docs/development/performance.md +++ b/docs/development/performance.md @@ -780,9 +780,9 @@ It deterministically generates 20,000, 200,000, and 800,000 the official sorted min/mean/max output contract with UTF-8 station names, and emits the existing `size=ms/op` contract. Dataset construction is outside the timed region. The benchmark's -[README](../../benchmarks/runtime-challenges/temperature-aggregation/README.md) +[README](https://github.com/Codevetter/codevetter/blob/main/benchmarks/runtime-challenges/temperature-aggregation/README.md) contains the exact diagnosis command, while its -[artifact record](../../benchmarks/runtime-challenges/temperature-aggregation/ARTIFACT.md) +[artifact record](https://github.com/Codevetter/codevetter/blob/main/benchmarks/runtime-challenges/temperature-aggregation/ARTIFACT.md) records attribution and the differences from the official Java challenge. The initial qualification used CodeVetter to select the parser at 77.64% CPU @@ -913,7 +913,7 @@ pnpm --silent runtime:campaign -- promote \ The same six campaign operations are available from `runtime:mcp`. Start that server with `--repo /path/to/candidate --incumbent-repo /path/to/incumbent` so the promotion checkout is fixed outside tool arguments. The checked-in -[agent program](../../scripts/runtime-failure-capsule/AUTONOMOUS_OPTIMIZATION_PROGRAM.md) +[agent program](https://github.com/Codevetter/codevetter/blob/main/scripts/runtime-failure-capsule/AUTONOMOUS_OPTIMIZATION_PROGRAM.md) defines the loop and its authority boundary. Campaign artifacts are local JSON under the declared directory: one manifest, diff --git a/docs/development/verification-receipts.md b/docs/development/verification-receipts.md index 9f3a1ab5..e5c37e77 100644 --- a/docs/development/verification-receipts.md +++ b/docs/development/verification-receipts.md @@ -2,7 +2,7 @@ title: Verification receipts description: Machine-first ingestion and comparison of project-owned test and performance evidence. sidebar: - order: 4 + order: 5 --- # Verification receipts @@ -32,7 +32,7 @@ The canonical producer schema is - limitations that bound the producer's claims. The complete executable example is -[`codevetter-local-fast-2026-08-07.json`](../../scripts/verification-receipts/fixtures/codevetter-local-fast-2026-08-07.json). +[`codevetter-local-fast-2026-08-07.json`](https://github.com/Codevetter/codevetter/blob/main/scripts/verification-receipts/fixtures/codevetter-local-fast-2026-08-07.json). That projection intentionally remains `no_confidence` for inventory because its source receipt contains aggregate outcomes instead of individual test and selection identities. Its command-level RSS is retained as partial evidence, diff --git a/docs/knowledge/performance-lab-publication.md b/docs/knowledge/performance-lab-publication.md index c40103c1..d68f54e5 100644 --- a/docs/knowledge/performance-lab-publication.md +++ b/docs/knowledge/performance-lab-publication.md @@ -55,7 +55,7 @@ product's end game. Merge status does not upgrade evidence strength: the directional, unverified, guardrail, and synthetic-stress entries are not independent claims of proven customer impact. The full inactive-project disposition is retained in -[`evidence/performance/old-local-projects-results-2026-08-09.md`](../../evidence/performance/old-local-projects-results-2026-08-09.md). +[`evidence/performance/old-local-projects-results-2026-08-09.md`](https://github.com/Codevetter/codevetter/blob/main/evidence/performance/old-local-projects-results-2026-08-09.md). ## External open-source work diff --git a/docs/knowledge/tooling-decisions.md b/docs/knowledge/tooling-decisions.md index 61057b85..aff07535 100644 --- a/docs/knowledge/tooling-decisions.md +++ b/docs/knowledge/tooling-decisions.md @@ -26,7 +26,18 @@ decay — re-check before acting on anything here. Items the research could not confirm are marked UNVERIFIED on the detail pages and should be treated as open questions, not findings. -## Adopt +## Status vocabulary + +- **Approved** — qualified for a bounded lane, but not executable there yet. +- **Trialled** — exercised against this repository with measured output, but not + part of a maintained path. +- **Wired** — invoked by a tracked local command, hook, workflow, or product + adapter. +- **Rejected** — disqualified for the named lane. Rejection in the private + customer-code product lane does not automatically reject a public-repository + maintainer aid. + +## Product adoption | Tool | License | Surface | Category detail | |---|---|---|---| @@ -38,10 +49,48 @@ questions, not findings. | Terminal-Bench 4.0 (Harbor) | Apache-2.0 | Corpus under `benchmarks/` | [agent benchmarks](./tooling-agent-benchmarks.md) | | `libkrun` | Apache-2.0 | In-process VMM in `src-tauri/` | [sandboxing](./tooling-sandboxing.md) | -`cargo-audit` is the highest-leverage item: the `rustsec` crate runs inside the -existing Rust backend with no new process boundary, no sidecar to codesign, and -no subprocess. Its SARIF 2.1.0 output is real but undocumented in the README and -absent from the changelog — pin `>= 0.22.0` and trust the source, not the docs. +These are product-lane decisions, not implementation receipts. In particular, +Gitleaks is now **wired** for this repository's hooks and CI, while bundling it +as a signed desktop sidecar remains **approved** work. + +## Repository and maintainer tooling + +| Tool | Status | Bounded use | +|---|---|---| +| DeepWiki MCP | Wired | Maintainer questions about this public repository; a new Codex session is required after MCP configuration changes | +| GitHub CodeQL default setup | Wired | GitHub-hosted scanning of this public repository only | +| Biome SARIF | Wired | Local artifact generation plus code-scanning upload | +| Gitleaks 8.30.1 | Wired | Staged-change hook, full-history local check, and checksum-pinned CI binary | +| zizmor 1.29 / action 0.6.2 | Wired | Offline local workflow audit plus GitHub code-scanning upload | +| actionlint 1.7.12 + ShellCheck 0.11.0 | Wired | Workflow syntax/semantics and embedded-shell validation; checksum-pinned CI binaries and local `pnpm quality:workflows` command | +| cargo-deny 0.20.2 | Wired | Offline Rust license, source, wildcard-requirement, and duplicate-version policy; native SARIF for actionable policy violations | +| ast-grep 0.45.2 | Trialled, not wired | Structural locations were correct, but native `--format sarif` emits an invalid root format version; no missing-rule case justifies a converter yet | +| Trivy 0.74.0 config scan | Trialled, not wired | Embedded checks found no supported first-party IaC surface; the unbounded scan targeted a dependency Dockerfile and the bounded scan misclassified a JSON fixture | +| OSV-Scanner 2.5.1 | Repository runner wired | `pnpm quality:vulnerabilities` produces offline SARIF plus a database-identity receipt; the 35-package baseline and remediation are tracked in issue #195 | +| StrykerJS 10.0.0 | Bounded local command wired | Accounting oracle: 218 mutants, 185 killed, 33 survived, 84.86% score; `pnpm quality:mutation:accounting`, tracked in issue #196 | +| Schemathesis 4.25.2 | Rejected for current surface | CLI availability verified, but CodeVetter has no OpenAPI/Swagger contract or HTTP server to exercise | +| Apple `container` CLI | Approved for measured trial | Host qualifies (arm64, macOS 27), but the signed admin-installed system service is absent; tracked in issue #197 | + +These tools subsidize discovery and evidence collection. CodeVetter still owns +receipt qualification, taxonomy, budgets, and the final measurable verdict. + +The first CodeQL run also exposed a cleartext localStorage API-key field. Source +tracing showed that the old browser gateway execution path had already been +removed and only its non-functional Settings panel remained. The remediation +removes that panel and allowlist-migrates the shared record to rubric fields +instead of adding a credential dependency to preserve dead behavior; issue +#194 remains open until pushed CodeQL evidence confirms the alert is closed. + +The OSV baseline changes the order of operations for repository dependency work: +fix or classify the measured lockfile baseline before adding another scanner. +The tracked [baseline evidence](https://github.com/Codevetter/codevetter/blob/main/evidence/security/osv-baseline-2026-08-31.md) +records 52 advisory/package matches and the exact database hashes; generated +SARIF and receipts remain ignored scratch. +`cargo-audit` remains the highest-leverage product embedding candidate because +the `rustsec` crate runs inside the existing Rust backend with no process +boundary or sidecar to codesign. Its SARIF 2.1.0 output is real but undocumented +in the README and absent from the changelog — pin `>= 0.22.0` and trust the +source, not the docs. ## Reject @@ -54,8 +103,8 @@ absent from the changelog — pin `>= 0.22.0` and trust the source, not the docs | npm / pnpm audit | No offline mode; npm's fallback path uploads the full dependency tree plus machine metadata | | `lcov` / `genhtml` Perl tooling | GPL-2.0 — do not bundle (the *format* is unencumbered) | | Meta OpenApps | CC-BY-NC-4.0, commercial use prohibited | -| DeepWiki | Hosted; private repos need a paid Devin account — see [documentation tooling](./tooling-documentation.md) | -| CodeQL | License forbids use on non-open-source codebases without paid GHAS — see [sandboxing](./tooling-sandboxing.md) | +| DeepWiki as product/docs authority | Hosted; private repos need a paid Devin account — see [documentation tooling](./tooling-documentation.md) | +| CodeQL on customer repositories | License forbids use on non-open-source codebases without paid GHAS — see [sandboxing](./tooling-sandboxing.md) | | Firecracker, gVisor | Linux-kernel only; no macOS host mode exists | | Docker Desktop | Proprietary GUI app, license-gated at 250 employees / $10M revenue | | WorkArena, WebVoyager | Require live third-party websites or hosted SaaS | diff --git a/docs/knowledge/tooling-documentation.md b/docs/knowledge/tooling-documentation.md index 7e014b72..930e3101 100644 --- a/docs/knowledge/tooling-documentation.md +++ b/docs/knowledge/tooling-documentation.md @@ -1,18 +1,23 @@ --- -title: Documentation tooling — the DeepWiki decision -description: Why DeepWiki is not adopted for CodeVetter's documentation, and what would fit if auto-generated architecture docs are wanted. +title: Documentation tooling — the DeepWiki boundary +description: Where DeepWiki helps CodeVetter maintainers, where it is rejected, and what would fit for generated architecture docs. sidebar: order: 14 --- -# Documentation tooling — the DeepWiki decision +# Documentation tooling — the DeepWiki boundary Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for the cross-category summary. -**Verdict: reject** DeepWiki as a documentation pipeline dependency. The only -form that composes with the existing stack is a passive outbound link (a README -or landing badge to `deepwiki.com/Codevetter/codevetter`), not an integration. +**Verdict: wired as a maintainer query aid; rejected as a documentation pipeline +or product dependency.** The public DeepWiki MCP endpoint is configured in the +owner's Codex environment for questions about this public repository. It is +not tracked repository configuration, does not ingest customer repositories, +and does not replace committed documentation or executable proof. + +Codex discovers MCP servers at session startup. After adding or changing the +DeepWiki entry, start a new session before claiming the tools are callable. ## Why @@ -41,8 +46,8 @@ not the hosted product. ### It fails on process grounds independently of privacy -Even setting privacy aside, DeepWiki conflicts with this repo's own documented -rules: +Promoting its generated pages to product documentation would still conflict +with this repo's own documented rules: - **"Markdown under `docs/` is the source of truth."** A hosted generated wiki is a second, non-authoritative home for the same facts. @@ -57,8 +62,8 @@ assessed DeepWiki in **April 2026** and reached the same conclusion. That document sat in a top-level `research/` directory that nothing linked to, so the finding was effectively lost and the question got asked again. -The August 2026 re-check confirmed the April verdict still holds, and that the -three self-hosted alternatives it named remain active: +The August 2026 re-check confirmed the product/docs boundary still holds, and +that the three self-hosted alternatives it named remain active: | Project | Stars | License | Last push | |---|---|---|---| diff --git a/docs/operations/ci.md b/docs/operations/ci.md index 9daad516..099831d2 100644 --- a/docs/operations/ci.md +++ b/docs/operations/ci.md @@ -7,15 +7,15 @@ sidebar: # CI -`.github/workflows/ci.yml` runs on every push and pull request. +`.github/workflows/ci.yml` runs on every push to `main` and every pull request. ## Job: `lint-and-typecheck` (ubuntu-latest) Steps, in order (a failure stops the job): -1. `actions/checkout@v6` -2. `pnpm/action-setup@v4` + `actions/setup-node@v6` (Node 22, pnpm cache) -3. `dtolnay/rust-toolchain@stable` +1. SHA-pinned `actions/checkout` with persisted credentials disabled +2. SHA-pinned `pnpm/action-setup` + `actions/setup-node` (Node 22, pnpm cache) +3. SHA-pinned `dtolnay/rust-toolchain` 4. Install Tauri Linux deps (`libwebkit2gtk-4.1-dev`, `libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`) 5. `pnpm install --frozen-lockfile` 6. **Lint** — `pnpm run lint` in `apps/desktop` (Biome) @@ -46,8 +46,35 @@ Steps, in order (a failure stops the job): | `deploy-landing.yml` | `workflow_dispatch` (manual) | deploy Astro site to Cloudflare Pages | [landing-deploy.md](./landing-deploy.md) | | `weekly.yml` | cron `0 9 * * 1` (Mon 09:00 UTC) + `workflow_dispatch` | lockfile-agnostic quality check (lint/typecheck/test/build if defined) | [jobs/weekly-quality.md](./jobs/weekly-quality.md) | | `docs.yml` | push/PR | doc link + structure validation | [../development/docs.md](../development/docs.md) | +| `repository-security.yml` | push to `main`/PR/manual | actionlint + ShellCheck, Biome SARIF, cargo-deny Rust policy, full-history Gitleaks, and zizmor code-scanning uploads | [../knowledge/tooling-decisions.md](../knowledge/tooling-decisions.md) | +| `osv-offline.yml` | manual | explicit OSV database refresh followed by a separate offline lockfile scan and evidence upload | [../knowledge/tooling-secrets-and-supply-chain.md](../knowledge/tooling-secrets-and-supply-chain.md) | + +`repository-security.yml` uses no application dependency. Gitleaks, actionlint, +ShellCheck, and cargo-deny are checksum-pinned binaries; every third-party +action is pinned to a commit; and job permissions are declared narrowly. +Publish jobs do not use dependency or toolchain caches, so an offline pedantic +zizmor review has no unsuppressed security findings. Its two remaining +informational suggestions prefer runner shell commands over the pinned Rust +toolchain setup action; the maintained, commit-pinned action is retained +deliberately. + +Run `pnpm quality:workflows` when actionlint is installed. It parses every +workflow, validates GitHub expression and event semantics, and delegates shell +fragments to ShellCheck. The first qualified audit and exact artifact identities +are recorded in [the tracked evidence](https://github.com/Codevetter/codevetter/blob/main/evidence/security/actionlint-baseline-2026-08-31.md). ## Local pre-commit / pre-push -- **pre-commit** (`.husky/pre-commit`): `lint-staged` → `biome check --write` on staged `apps/desktop/src/**/*.{ts,tsx}`. -- **pre-push** (`.husky/pre-push`): `npm run lint` (root `lint` script, which runs Biome) + secret-pattern scan over tracked files. Exclusions are anchored to known dirs (`benchmark/`, `apps/landing-page-astro/public/benchmark/`, fixtures, `secret_policy.rs`). +- **pre-commit** (`.husky/pre-commit`): `lint-staged` → `biome check --write` on staged `apps/desktop/src/**/*.{ts,tsx}`, then Gitleaks staged-diff scanning when the binary is installed. +- **pre-push** (`.husky/pre-push`): `pnpm run lint`, then full-history Gitleaks. A limited tracked-file pattern scan remains as a fallback for contributors without the binary; CI always uses Gitleaks. + +Generate a local Biome SARIF 2.1.0 artifact at +`artifacts/tooling/biome.sarif` with `pnpm quality:sarif`. Generated artifacts +remain ignored scratch; the workflow uploads them to GitHub code scanning. + +Run `pnpm quality:vulnerabilities` after explicitly refreshing the OSV databases. +The scan itself is offline and produces `artifacts/tooling/osv/results.sarif` +plus a receipt containing scanner, source, and database identities. Exit `1` +means findings; exit `2` means the scanner or evidence path failed. This is not +yet a push/PR gate because the measured baseline must be remediated rather than +silently accepted. The manual workflow remains red while findings exist. diff --git a/evidence/security/actionlint-baseline-2026-08-31.md b/evidence/security/actionlint-baseline-2026-08-31.md new file mode 100644 index 00000000..3b446dff --- /dev/null +++ b/evidence/security/actionlint-baseline-2026-08-31.md @@ -0,0 +1,45 @@ +# Actionlint and ShellCheck qualification — 2026-08-31 + +This receipt qualifies repository workflow validation; it is not product +runtime evidence. + +## Tool identity + +| Tool | Version | License | Qualified artifact | +|---|---|---|---| +| actionlint | 1.7.12 | MIT | Darwin arm64 archive SHA-256 `aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f` | +| ShellCheck | 0.11.0 | GPL-3.0 | Linux x86-64 archive SHA-256 `b7af85e41cc99489dcc21d66c6d5f3685138f06d34651e6d34b42ec6d54fe6f6` | + +The CI lane uses actionlint's publisher-provided Linux x86-64 checksum +`8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8`. +ShellCheck's release does not publish a checksum file; its hardcoded digest was +qualified from the GitHub release asset fetched through the authenticated +GitHub API. + +## First audit + +The initial full-workflow audit found three findings: + +- `ci.yml`: SC2046 identified an unquoted computed ccusage binary path. The + target is now captured separately and the complete executable path is quoted. +- `weekly.yml`: two SC2129 style findings identified repeated redirects to + `GITHUB_OUTPUT` and `GITHUB_STEP_SUMMARY`. Each set now uses one bounded group + redirect. + +The complete rerun exits zero with no ignored rules. The permanent +`repository-security.yml` job downloads both exact binaries, verifies their +hashes, and runs actionlint with ShellCheck discovery enabled. No application +dependency or product-runtime invocation was added. + +## Companion pedantic zizmor audit + +Running zizmor 1.29.0 with `--offline --pedantic` after actionlint exposed one +remaining shell-template expansion, workflow-level write permissions, missing +concurrency controls, undocumented permission purposes, and unnamed jobs. The +workflows now pass the structural findings. The remaining output is the four +low-confidence cache-poisoning heuristics on trusted deploy/release workflows +plus two informational suggestions to replace the pinned Rust toolchain action +with runner shell commands. The publish jobs now disable or omit dependency and +toolchain caches, eliminating all four cache findings. The informational +suggestions were not applied: retaining one maintained, commit-pinned setup +action is clearer than duplicating toolchain setup shell logic. diff --git a/evidence/security/ast-grep-sarif-qualification-2026-08-31.md b/evidence/security/ast-grep-sarif-qualification-2026-08-31.md new file mode 100644 index 00000000..b3ef72e6 --- /dev/null +++ b/evidence/security/ast-grep-sarif-qualification-2026-08-31.md @@ -0,0 +1,40 @@ +# ast-grep SARIF qualification — 2026-08-31 + +## Scope + +This trial evaluates ast-grep's native SARIF output before considering it for +repository code-scanning upload. It does not evaluate ast-grep's matching +engine generally and does not add a rule pack. + +## Tool and fixture + +- Tool: ast-grep 0.45.2 +- License: MIT +- Rule: an inline TypeScript pattern matching + `localStorage.setItem($KEY, $VALUE)` +- Target: `apps/desktop/src/lib/review-service.ts` + +The matcher found both expected calls with exact file, line, column, byte, and +snippet locations. That establishes useful structural-match fidelity for this +fixture. + +## SARIF result + +The `--format sarif` envelope is not valid SARIF 2.1.0: + +- the root `version` is `0.45.2`, which is the ast-grep tool version rather + than the required SARIF format version `2.1.0`; +- the root `$schema` declaration is absent; +- the tool driver omits its own version even though that information was + written into the incompatible root field. + +Locations, rule IDs, severity, and messages are otherwise present. The output +should not be uploaded to GitHub code scanning or treated as interoperable +SARIF without a repository-owned repair step and schema validation. + +## Decision + +Trialled, not wired. Biome and CodeQL already cover the current repository +lanes. Add ast-grep only when a concrete structural rule closes a demonstrated +gap, and either upstream emits a conforming envelope or a bounded converter is +qualified with fixtures. diff --git a/evidence/security/cargo-deny-baseline-2026-08-31.md b/evidence/security/cargo-deny-baseline-2026-08-31.md new file mode 100644 index 00000000..204ab567 --- /dev/null +++ b/evidence/security/cargo-deny-baseline-2026-08-31.md @@ -0,0 +1,54 @@ +# cargo-deny policy qualification — 2026-08-31 + +## Scope + +This receipt qualifies cargo-deny as a repository policy gate for the Rust +desktop dependency graph. It does not add cargo-deny to the shipped desktop +binary and does not fetch an advisory database during the check. + +## Tool identity + +| Tool | Version | License | Qualified artifacts | +|---|---:|---|---| +| cargo-deny | 0.20.2 | MIT OR Apache-2.0 | macOS arm64 SHA-256 `fe67d82a10d8597a3549364cb733a3f9cc1bfff9031b7ae46384a9f2a72090c3`; CI Linux x86-64 musl SHA-256 `9f12ed4c49936e09b48bf862b595cde2fe64fcbd9d74dfacac6131ca824c8d5f` | + +Both digests match the release assets published for cargo-deny 0.20.2. The +macOS archive digest was reproduced after download before executing the binary. + +## Policy + +The tracked `apps/desktop/src-tauri/deny.toml` policy: + +- evaluates the shipped `aarch64-apple-darwin` graph; +- allows the 15 permissive or file-level-copyleft SPDX families currently + present in the graph; +- ignores unpublished workspace packages and marks the desktop crate + `publish = false` so it cannot be published accidentally; +- denies wildcard dependency requirements; +- denies unknown registries and all Git dependencies; +- reports duplicate transitive versions as warnings rather than pretending + Tauri-owned convergence is immediately actionable. + +## Baseline result + +The offline, locked policy run exits zero: + +```text +bans ok, licenses ok, sources ok +``` + +The graph contains 14 duplicate-version warnings. They are visible in command +output but are not uploaded as code-scanning alerts. The SARIF lane covers +license and source-policy violations, while the bans lane remains an enforced +human-readable check. cargo-deny 0.20.2 emits native SARIF 2.1.0; this corrects +the earlier documentation claim that only JSON output was available. + +## Maintained path + +- `pnpm quality:rust-policy` runs the full offline policy when the pinned tool + is installed locally. +- `repository-security.yml` downloads the exact Linux archive, verifies its + SHA-256, uploads license/source SARIF, and separately enforces bans. +- RustSec advisory scanning remains in the OSV lane until the existing + vulnerability baseline is remediated; this policy gate does not duplicate + or suppress those findings. diff --git a/evidence/security/osv-baseline-2026-08-31.md b/evidence/security/osv-baseline-2026-08-31.md new file mode 100644 index 00000000..e361d2b8 --- /dev/null +++ b/evidence/security/osv-baseline-2026-08-31.md @@ -0,0 +1,55 @@ +# OSV offline baseline — 2026-08-31 + +This is a qualified repository-security baseline, not a product verdict and not +an allowlist. It records what OSV-Scanner observed so remediation can be +measured without hiding existing findings. + +## Reproduction + +- Source revision: `225480e5b6a018014bef1c190ed8ac3914a472e7` +- Scanner: OSV-Scanner `2.5.1` +- Command: `pnpm quality:vulnerabilities` +- Network during scan: disabled +- Warm scan duration: 8.942 seconds +- Machine output: ignored `artifacts/tooling/osv/results.sarif` +- Receipt: ignored `artifacts/tooling/osv/receipt.json` + +The command exits `0` when clean, `1` when findings exist, and `2` for an +operational failure. Database refresh is deliberately excluded from the scan. + +## Database identity + +| Ecosystem | Bytes | SHA-256 | +|---|---:|---| +| crates.io | 3,415,816 | `e276fb0061eefcb63ce5ba63b6888640ccc809dc195a698af7b7dbaaeaa9a02c` | +| Go | 11,481,601 | `49b2410b903ae009b3a89ab9be5245d531d0c176b6809931f2182b5ee6bc1204` | +| npm | 221,667,534 | `d89fbb49609224a0ecdb5cd14d6b874ab105197a5e83543f78450bab406cceb8` | + +## Findings + +- 35 affected package versions across two lockfiles +- 52 advisory/package matches +- 49 unique primary advisory IDs in OSV JSON +- 48 unique SARIF rules after alias normalization +- SARIF severity by unique rule: 0 critical, 11 high, 18 medium, 2 low, + 17 without a numeric severity +- Root `pnpm-lock.yaml` and the Go module were clean + +`docs-site/pnpm-lock.yaml` contains 17 affected package versions and 33 +advisory/package matches. They are transitive dependencies of the single direct +docs-site dependency, Blume `1.0.4`. Upgrading Blume is a production/build +dependency change and requires owner approval. + +`apps/desktop/src-tauri/Cargo.lock` contains 18 affected package versions and 19 +advisory/package matches. Most are unmaintained GTK3 bindings reached through +Tauri's Linux target graph. `event-listener` is reached through the Linux +notification stack. The `unic-*` crates are reached through +`urlpattern -> tauri-utils` and are not classified as Linux-only. These are +reachability notes, not suppressions. + +## Gate decision + +Do not add a second vulnerability scanner or silently baseline these findings. +First test the Blume update and bounded Cargo lockfile remediation, then repeat +this exact offline scan. CI enforcement should follow remediation so the gate +does not normalize known high-severity results. diff --git a/evidence/security/trivy-config-qualification-2026-08-31.md b/evidence/security/trivy-config-qualification-2026-08-31.md new file mode 100644 index 00000000..ea433ef7 --- /dev/null +++ b/evidence/security/trivy-config-qualification-2026-08-31.md @@ -0,0 +1,45 @@ +# Trivy configuration-scan qualification — 2026-08-31 + +## Scope + +This trial asks whether Trivy's embedded misconfiguration checks add a useful +repository lane without downloading databases or overlapping the offline OSV +scanner. It does not qualify Trivy vulnerability scanning or product bundling. + +## Tool and safety flags + +- Tool: Trivy 0.74.0 +- License: Apache-2.0 +- Command lane: `trivy config` +- Explicit controls: `--disable-telemetry`, `--skip-version-check`, and + `--skip-check-update` + +Debug output confirmed that the notification/version request was skipped, no +downloadable checks were loaded, and 563 embedded checks were used. This is +command-level evidence, not packet-capture proof of zero outbound traffic. + +## Results + +The unbounded repository scan found only a Dockerfile inside +`docs-site/node_modules/yaml-language-server` and reported two findings against +that dependency-owned file. After excluding dependency, build, target, and +artifact directories, Trivy detected one file: + +`apps/desktop/tests/fixtures/warm-verification/differential-runtime-qualification-current.json` + +It classified that product test fixture as CloudFormation, reported 24 passing +checks and zero failures, and found no first-party Dockerfile, Terraform, +Kubernetes, Helm, Ansible, Azure ARM, or CloudFormation surface to protect. + +The SARIF 2.1.0 envelope itself is structurally complete. In JSON debug output, +Trivy also includes repository URL, branch, commit, author, and committer +metadata, which is acceptable for this public-repository trial but must be +considered before any private-code product use. + +## Decision + +Trialled, not wired. A permanent lane would currently scan dependency-owned +files or misclassify a qualification fixture while protecting no supported +first-party infrastructure configuration. Re-evaluate when the repository adds +a real supported IaC or container surface. Keep all three network-suppression +flags mandatory in any future trial. diff --git a/evidence/verification/stryker-accounting-oracle-2026-08-31.md b/evidence/verification/stryker-accounting-oracle-2026-08-31.md new file mode 100644 index 00000000..0204cabe --- /dev/null +++ b/evidence/verification/stryker-accounting-oracle-2026-08-31.md @@ -0,0 +1,30 @@ +# Accounting-oracle mutation qualification — 2026-08-31 + +StrykerJS was evaluated against +`scripts/qualify-codex-accounting-oracle.mjs`, a deterministic verification +boundary where a false pass would corrupt CodeVetter's accounting evidence. + +| Measurement | Initial trial | Strengthened suite | +|---|---:|---:| +| StrykerJS | 10.0.0 | 10.0.0 | +| Mutants | 218 | 218 | +| Killed | 88 | 185 | +| Survived | 130 | 33 | +| Mutation score | 40.37% | 84.86% | +| Wall time | 14 seconds | 53 seconds | + +The added tests cover invalid and zero numeric evidence, inverted cost bounds, +provider selection, duplicate and missing daily buckets, CLI success/mismatch/ +malformed-input exits, and the exact CodexBar subprocess arguments and +`CODEX_HOME` handoff. + +The 33 survivors are primarily error-message string changes and equivalent or +low-value implementation mutations. Remaining behavioral cases include exact +epsilon boundaries, deterministic multi-date ordering, and a forced CodexBar +non-zero exit. They remain visible in the ignored JSON report rather than being +excluded from mutation. + +The maintained local command is `pnpm quality:mutation:accounting`. It uses +ephemeral, exactly-versioned StrykerJS and TypeScript packages, writes its report +under ignored `artifacts/tooling/stryker/`, and fails below 80%. It is deliberately +bounded to one high-value oracle rather than applied as a universal score. diff --git a/knip.json b/knip.json index 61c94e41..df851436 100644 --- a/knip.json +++ b/knip.json @@ -34,8 +34,11 @@ "ccusage" ], "ignoreBinaries": [ + "actionlint", + "cargo-deny", "du", "go", + "gitleaks", "lipo", "lsof", "netstat", diff --git a/package.json b/package.json index 2af9fe24..1ec73f53 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,13 @@ "quality:complexity": "node scripts/check-changed-complexity.mjs", "quality:cycles": "biome lint --only=suspicious/noImportCycles .", "quality:dependencies": "pnpm audit --audit-level high", + "quality:sarif": "node scripts/run-biome-sarif.mjs", + "quality:secrets": "gitleaks git --no-banner --redact=100 .", + "quality:secrets:staged": "gitleaks git --pre-commit --staged --no-banner --redact=100 .", + "quality:mutation:accounting": "pnpm --package @stryker-mutator/core@10.0.0 --package typescript@5.9.3 dlx stryker run scripts/stryker-accounting.config.mjs", + "quality:rust-policy": "cargo-deny --manifest-path apps/desktop/src-tauri/Cargo.toml --config apps/desktop/src-tauri/deny.toml --frozen check --hide-inclusion-graph licenses sources bans", + "quality:vulnerabilities": "node scripts/run-osv-offline.mjs", + "quality:workflows": "actionlint", "quality:duplication": "jscpd apps/desktop/src apps/landing-page-astro/src scripts --min-lines 8 --min-tokens 60 --mode strict --format typescript,tsx,javascript --cross-formats js-ts --ignore '**/fixtures/**,**/generated/**,**/gen/**,**/node_modules/**,**/dist/**,**/out/**,**/coverage/**' --threshold 0.81 --reporters console,threshold --no-colors --no-tips", "retrieval:discover": "node scripts/context-retrieval/discover-candidates.mjs --registry benchmarks/context-retrieval/candidates.json --fresh-since 2026-02-22" }, diff --git a/scripts/merge-landing-docs.mjs b/scripts/merge-landing-docs.mjs index afda18f0..4d0d2b69 100644 --- a/scripts/merge-landing-docs.mjs +++ b/scripts/merge-landing-docs.mjs @@ -113,8 +113,17 @@ function removeDuplicatePageTitle(html) { return { html: after, removed: 1 }; } +function hasSiteOrigin(value) { + return ( + value === SITE_ORIGIN || + value.startsWith(`${SITE_ORIGIN}/`) || + value.startsWith(`${SITE_ORIGIN}?`) || + value.startsWith(`${SITE_ORIGIN}#`) + ); +} + function canonicalDocsURL(value) { - const prefix = value.startsWith(SITE_ORIGIN) ? SITE_ORIGIN : ''; + const prefix = hasSiteOrigin(value) ? SITE_ORIGIN : ''; const relativeURL = prefix ? value.slice(prefix.length) : value; const { pathname, suffix } = splitURLSuffix(relativeURL); if (pathname === '/docs/index' || pathname === '/docs/index/') { @@ -156,7 +165,7 @@ function normalizeDocsURLs(directory) { } function localDocsRoute(rawHref, currentRoute) { - const localHref = rawHref.startsWith(SITE_ORIGIN) ? rawHref.slice(SITE_ORIGIN.length) : rawHref; + const localHref = hasSiteOrigin(rawHref) ? rawHref.slice(SITE_ORIGIN.length) : rawHref; if (localHref.startsWith('#') || localHref.startsWith('//') || /^(?:[a-z]+:)/i.test(localHref)) { return null; } diff --git a/scripts/qualify-codex-accounting-oracle.test.mjs b/scripts/qualify-codex-accounting-oracle.test.mjs index 32cbb857..c0ce03a8 100644 --- a/scripts/qualify-codex-accounting-oracle.test.mjs +++ b/scripts/qualify-codex-accounting-oracle.test.mjs @@ -1,5 +1,10 @@ import assert from 'node:assert/strict'; -import test from 'node:test'; +import { spawnSync } from 'node:child_process'; +import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import test, { after, before } from 'node:test'; +import { fileURLToPath } from 'node:url'; import { compareAccounting, @@ -49,11 +54,56 @@ const actual = { ], }; +let fixtureDirectory; +let fakeCodexBarPath; +const scriptPath = fileURLToPath(new URL('./qualify-codex-accounting-oracle.mjs', import.meta.url)); + +before(() => { + fixtureDirectory = mkdtempSync(join(tmpdir(), 'codevetter-accounting-oracle-')); + fakeCodexBarPath = join(fixtureDirectory, 'fake-codexbar.mjs'); + writeFileSync( + fakeCodexBarPath, + `#!/usr/bin/env node +const expected = ['cost', '--provider', 'codex', '--json', '--refresh']; +if (JSON.stringify(process.argv.slice(2)) !== JSON.stringify(expected)) { + process.stderr.write('unexpected arguments'); + process.exit(9); +} +if (!process.env.CODEX_HOME?.endsWith('/codex-home')) { + process.stderr.write('missing CODEX_HOME'); + process.exit(10); +} +process.stdout.write(${JSON.stringify(JSON.stringify(oracle))}); +` + ); + chmodSync(fakeCodexBarPath, 0o755); +}); + +after(() => { + rmSync(fixtureDirectory, { recursive: true, force: true }); +}); + test('accepts exact aggregate and local-day token parity', () => { const mismatches = compareAccounting(normalizeCodexBar(oracle), normalizeCodeVetter(actual)); assert.deepEqual(mismatches, []); }); +test('accepts zero-valued integer and cost evidence', () => { + const zero = { + totals: { + input_tokens: 0, + cache_read_tokens: 0, + output_tokens: 0, + total_tokens: 0, + api_equivalent_cost_usd: 0, + }, + }; + const normalized = normalizeCodeVetter(zero); + assert.equal(normalized.totals.input_tokens, 0); + assert.equal(normalized.totals.api_equivalent_cost_min_usd, 0); + assert.deepEqual([...normalized.daily], []); +}); + test('accepts an oracle cost inside bounded service-tier uncertainty', () => { const ranged = structuredClone(actual); delete ranged.totals.api_equivalent_cost_usd; @@ -99,3 +149,150 @@ test('rejects unsafe or duplicate input instead of coercing it', () => { unsafe.totals.inputTokens = Number.MAX_SAFE_INTEGER + 1; assert.throws(() => normalizeCodexBar(unsafe), /safe integer/); }); + +test('rejects every invalid numeric shape and inverted cost bounds', () => { + for (const value of [-1, 1.5, Number.NaN, Number.POSITIVE_INFINITY, '1']) { + const invalid = structuredClone(actual); + invalid.totals.input_tokens = value; + assert.throws(() => normalizeCodeVetter(invalid), /non-negative safe integer/); + } + + for (const value of [-0.01, Number.NaN, Number.POSITIVE_INFINITY, '0.25']) { + const invalid = structuredClone(actual); + invalid.totals.api_equivalent_cost_usd = value; + assert.throws(() => normalizeCodeVetter(invalid), /non-negative finite number/); + } + + const inverted = structuredClone(actual); + delete inverted.totals.api_equivalent_cost_usd; + inverted.totals.api_equivalent_cost_min_usd = 0.5; + inverted.totals.api_equivalent_cost_max_usd = 0.2; + assert.throws(() => normalizeCodeVetter(inverted), /minimum must not exceed maximum/); +}); + +test('requires the local Codex provider and valid unique daily dates', () => { + assert.deepEqual( + normalizeCodexBar([undefined, { provider: 'other' }, oracle]), + normalizeCodexBar(oracle) + ); + assert.throws(() => normalizeCodexBar([]), /local codex provider row/); + assert.throws( + () => normalizeCodexBar({ ...oracle, provider: 'other' }), + /local codex provider row/ + ); + assert.throws( + () => normalizeCodexBar({ ...oracle, source: 'remote' }), + /local codex provider row/ + ); + + const invalidDate = structuredClone(actual); + invalidDate.daily[0].date = 20260716; + assert.throws(() => normalizeCodeVetter(invalidDate), /date is missing or duplicated/); + + const duplicateOracleDate = structuredClone(oracle); + duplicateOracleDate.daily.push(structuredClone(oracle.daily[0])); + assert.throws(() => normalizeCodexBar(duplicateOracleDate), /oracle daily.*duplicated/); + assert.throws(() => normalizeCodeVetter(undefined), /actual\.totals\.input_tokens/); +}); + +test('reports missing daily buckets in either input', () => { + const missingActual = structuredClone(actual); + missingActual.daily = []; + assert.deepEqual( + compareAccounting(normalizeCodexBar(oracle), normalizeCodeVetter(missingActual)), + [ + { + scope: 'daily.2026-07-16', + field: 'bucket', + expected: 'present', + actual: 'missing', + }, + ] + ); + + const missingOracle = structuredClone(oracle); + missingOracle.daily = []; + assert.deepEqual( + compareAccounting(normalizeCodexBar(missingOracle), normalizeCodeVetter(actual)), + [ + { + scope: 'daily.2026-07-16', + field: 'bucket', + expected: 'missing', + actual: 'present', + }, + ] + ); +}); + +test('CLI distinguishes qualified, mismatched, and malformed evidence', () => { + const oraclePath = join(fixtureDirectory, 'oracle.json'); + const actualPath = join(fixtureDirectory, 'actual.json'); + writeFileSync(oraclePath, JSON.stringify(oracle)); + writeFileSync(actualPath, JSON.stringify(actual)); + + const qualified = spawnSync( + process.execPath, + [scriptPath, '--oracle-json', oraclePath, '--codevetter-json', actualPath], + { encoding: 'utf8' } + ); + assert.equal(qualified.status, 0); + assert.deepEqual(JSON.parse(qualified.stdout), { + qualified: true, + compared_token_fields: ['input_tokens', 'cache_read_tokens', 'output_tokens', 'total_tokens'], + compared_cost_field: 'oracle point must fall within CodeVetter API-equivalent bounds', + compared_daily_buckets: ['2026-07-16'], + reasoning_parity: 'not_exposed_by_codexbar_cli; qualified_by_internal_fixture', + mismatches: [], + }); + + const mismatchedActual = structuredClone(actual); + mismatchedActual.totals.total_tokens += 1; + writeFileSync(actualPath, JSON.stringify(mismatchedActual)); + const mismatched = spawnSync( + process.execPath, + [scriptPath, '--oracle-json', oraclePath, '--codevetter-json', actualPath], + { encoding: 'utf8' } + ); + assert.equal(mismatched.status, 1); + assert.equal(JSON.parse(mismatched.stdout).qualified, false); + + writeFileSync(actualPath, '{'); + const malformed = spawnSync( + process.execPath, + [scriptPath, '--oracle-json', oraclePath, '--codevetter-json', actualPath], + { encoding: 'utf8' } + ); + assert.equal(malformed.status, 2); + assert.match(malformed.stderr, /JSON/); +}); + +test('CLI invokes CodexBar with its exact local evidence contract', () => { + const actualPath = join(fixtureDirectory, 'actual-for-binary.json'); + const codexHome = join(fixtureDirectory, 'codex-home'); + writeFileSync(actualPath, JSON.stringify(actual)); + + const qualified = spawnSync( + process.execPath, + [ + scriptPath, + '--codex-home', + codexHome, + '--codexbar', + fakeCodexBarPath, + '--codevetter-json', + actualPath, + ], + { encoding: 'utf8' } + ); + assert.equal(qualified.status, 0, qualified.stderr); + assert.equal(JSON.parse(qualified.stdout).qualified, true); + + const missingHome = spawnSync( + process.execPath, + [scriptPath, '--codevetter-json', actualPath, '--codexbar', fakeCodexBarPath], + { encoding: 'utf8' } + ); + assert.equal(missingHome.status, 2); + assert.match(missingHome.stderr, /--codex-home is required/); +}); diff --git a/scripts/run-biome-sarif.mjs b/scripts/run-biome-sarif.mjs new file mode 100644 index 00000000..3b81be7f --- /dev/null +++ b/scripts/run-biome-sarif.mjs @@ -0,0 +1,22 @@ +#!/usr/bin/env node + +import { mkdirSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { spawnSync } from 'node:child_process'; + +const outputPath = resolve(process.env.BIOME_SARIF_PATH ?? 'artifacts/tooling/biome.sarif'); +mkdirSync(dirname(outputPath), { recursive: true }); + +const pnpm = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm'; +const result = spawnSync( + pnpm, + ['exec', 'biome', 'ci', '--reporter=sarif', `--reporter-file=${outputPath}`, '.'], + { stdio: 'inherit' } +); + +if (result.error) { + console.error(`Unable to run Biome: ${result.error.message}`); + process.exitCode = 1; +} else { + process.exitCode = result.status ?? 1; +} diff --git a/scripts/run-osv-offline.mjs b/scripts/run-osv-offline.mjs new file mode 100644 index 00000000..01cbf728 --- /dev/null +++ b/scripts/run-osv-offline.mjs @@ -0,0 +1,191 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { + closeSync, + existsSync, + mkdirSync, + openSync, + readSync, + readFileSync, + readdirSync, + renameSync, + statSync, + writeFileSync, +} from 'node:fs'; +import { homedir } from 'node:os'; +import { dirname, relative, resolve } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +export const RECEIPT_SCHEMA = 'codevetter.osv-offline-scan/v1'; + +const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)); +const REPOSITORY_ROOT = resolve(SCRIPT_DIR, '..'); +const DEFAULT_OUTPUT_DIR = resolve(REPOSITORY_ROOT, 'artifacts/tooling/osv'); + +function sha256File(path) { + const hash = createHash('sha256'); + const buffer = Buffer.alloc(1024 * 1024); + const descriptor = openSync(path, 'r'); + try { + let bytesRead = readSync(descriptor, buffer, 0, buffer.length, null); + while (bytesRead > 0) { + hash.update(buffer.subarray(0, bytesRead)); + bytesRead = readSync(descriptor, buffer, 0, buffer.length, null); + } + } finally { + closeSync(descriptor); + } + return hash.digest('hex'); +} + +export function defaultDatabaseRoot({ + platform = process.platform, + home = homedir(), + xdgCacheHome = process.env.XDG_CACHE_HOME, + localAppData = process.env.LOCALAPPDATA, +} = {}) { + if (platform === 'darwin') return resolve(home, 'Library/Caches/osv-scalibr'); + if (platform === 'win32' && localAppData) return resolve(localAppData, 'osv-scalibr'); + return resolve(xdgCacheHome ?? resolve(home, '.cache'), 'osv-scalibr'); +} + +export function parseScannerVersion(stdout) { + const match = stdout.match(/^osv-scanner version:\s*(\S+)/m); + if (!match) throw new Error('Unable to parse osv-scanner version output'); + return match[1]; +} + +export function classifyScannerExit(status) { + if (status === 0) return 'clean'; + if (status === 1) return 'findings'; + return 'operational_failure'; +} + +export function collectDatabaseIdentities(databaseRoot) { + if (!existsSync(databaseRoot)) return []; + return readdirSync(databaseRoot, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => ({ ecosystem: entry.name, path: resolve(databaseRoot, entry.name, 'all.zip') })) + .filter((entry) => existsSync(entry.path)) + .map(({ ecosystem, path }) => { + const stat = statSync(path); + return { + ecosystem, + sha256: sha256File(path), + bytes: stat.size, + modified_at: stat.mtime.toISOString(), + }; + }) + .sort((left, right) => left.ecosystem.localeCompare(right.ecosystem)); +} + +function gitRevision(repositoryRoot) { + const result = spawnSync('git', ['rev-parse', 'HEAD'], { + cwd: repositoryRoot, + encoding: 'utf8', + }); + if (result.status !== 0) throw new Error('Unable to resolve repository revision'); + return result.stdout.trim(); +} + +function sarifResultCount(path) { + const sarif = JSON.parse(readFileSync(path, 'utf8')); + return (sarif.runs ?? []).reduce((count, run) => count + (run.results?.length ?? 0), 0); +} + +function writeJsonAtomic(path, value) { + mkdirSync(dirname(path), { recursive: true }); + const temporary = `${path}.tmp-${process.pid}`; + writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`); + renameSync(temporary, path); +} + +export function runOfflineScan({ + repositoryRoot = REPOSITORY_ROOT, + outputDir = DEFAULT_OUTPUT_DIR, + databaseRoot = defaultDatabaseRoot(), + scanner = 'osv-scanner', +} = {}) { + const versionResult = spawnSync(scanner, ['--version'], { encoding: 'utf8' }); + if (versionResult.status !== 0) { + throw new Error('osv-scanner is unavailable; install the pinned qualified version first'); + } + + const databases = collectDatabaseIdentities(databaseRoot); + if (databases.length === 0) { + throw new Error('No offline OSV databases found; refresh them in an explicit network step'); + } + + mkdirSync(outputDir, { recursive: true }); + const sarifPath = resolve(outputDir, 'results.sarif'); + const receiptPath = resolve(outputDir, 'receipt.json'); + const startedAt = new Date(); + const scan = spawnSync( + scanner, + [ + 'scan', + 'source', + '--offline', + '--offline-vulnerabilities', + '--recursive', + '--format=sarif', + `--output-file=${sarifPath}`, + '--verbosity=warn', + '.', + ], + { cwd: repositoryRoot, encoding: 'utf8' } + ); + const finishedAt = new Date(); + const outcome = classifyScannerExit(scan.status); + + const receipt = { + schema: RECEIPT_SCHEMA, + tool: { + name: 'osv-scanner', + version: parseScannerVersion(versionResult.stdout), + }, + source: { + revision: gitRevision(repositoryRoot), + scan_root: '.', + recursive: true, + }, + execution: { + network: 'disabled', + vulnerability_source: 'preseeded-local-databases', + started_at: startedAt.toISOString(), + finished_at: finishedAt.toISOString(), + duration_ms: finishedAt.getTime() - startedAt.getTime(), + scanner_exit_code: scan.status, + outcome, + }, + databases, + artifact: existsSync(sarifPath) + ? { + path: relative(repositoryRoot, sarifPath), + sha256: sha256File(sarifPath), + result_count: sarifResultCount(sarifPath), + } + : null, + limitations: [ + 'Database refresh is intentionally outside this offline command.', + 'A lockfile advisory does not by itself establish runtime reachability.', + 'OSV result count may include aliases for the same underlying vulnerability.', + ], + }; + writeJsonAtomic(receiptPath, receipt); + + if (scan.stderr) process.stderr.write(scan.stderr); + process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`); + return scan.status ?? 2; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + try { + process.exitCode = runOfflineScan(); + } catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exitCode = 2; + } +} diff --git a/scripts/run-osv-offline.test.mjs b/scripts/run-osv-offline.test.mjs new file mode 100644 index 00000000..1393a799 --- /dev/null +++ b/scripts/run-osv-offline.test.mjs @@ -0,0 +1,70 @@ +import assert from 'node:assert/strict'; +import { mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import test from 'node:test'; + +import { + classifyScannerExit, + collectDatabaseIdentities, + defaultDatabaseRoot, + parseScannerVersion, + RECEIPT_SCHEMA, +} from './run-osv-offline.mjs'; + +test('defines a versioned receipt contract', () => { + assert.equal(RECEIPT_SCHEMA, 'codevetter.osv-offline-scan/v1'); +}); + +test('resolves platform cache roots without exposing them in evidence', () => { + assert.equal( + defaultDatabaseRoot({ platform: 'darwin', home: '/Users/example' }), + '/Users/example/Library/Caches/osv-scalibr' + ); + assert.equal( + defaultDatabaseRoot({ platform: 'linux', home: '/home/example' }), + '/home/example/.cache/osv-scalibr' + ); + assert.equal( + defaultDatabaseRoot({ + platform: 'linux', + home: '/home/example', + xdgCacheHome: '/cache', + }), + '/cache/osv-scalibr' + ); +}); + +test('parses scanner identity and keeps findings distinct from operational failure', () => { + assert.equal( + parseScannerVersion('osv-scanner version: 2.5.1\nosv-scalibr version: 0.5.2'), + '2.5.1' + ); + assert.equal(classifyScannerExit(0), 'clean'); + assert.equal(classifyScannerExit(1), 'findings'); + assert.equal(classifyScannerExit(2), 'operational_failure'); + assert.equal(classifyScannerExit(null), 'operational_failure'); +}); + +test('hashes databases by ecosystem without retaining absolute cache paths', (context) => { + const root = join(tmpdir(), `codevetter-osv-db-${process.pid}-${Date.now()}`); + context.after(() => { + // The test runner owns this unique temporary directory; removal is bounded. + rmSync(root, { recursive: true, force: true }); + }); + mkdirSync(join(root, 'npm'), { recursive: true }); + mkdirSync(join(root, 'crates.io'), { recursive: true }); + writeFileSync(join(root, 'npm', 'all.zip'), 'npm-db'); + writeFileSync(join(root, 'crates.io', 'all.zip'), 'rust-db'); + + const identities = collectDatabaseIdentities(root); + assert.deepEqual( + identities.map(({ ecosystem, bytes }) => ({ ecosystem, bytes })), + [ + { ecosystem: 'crates.io', bytes: 7 }, + { ecosystem: 'npm', bytes: 6 }, + ] + ); + assert.ok(identities.every((entry) => !('path' in entry))); + assert.ok(identities.every((entry) => /^[a-f0-9]{64}$/.test(entry.sha256))); +}); diff --git a/scripts/stryker-accounting.config.mjs b/scripts/stryker-accounting.config.mjs new file mode 100644 index 00000000..9d7af3f7 --- /dev/null +++ b/scripts/stryker-accounting.config.mjs @@ -0,0 +1,33 @@ +export default { + mutate: ['scripts/qualify-codex-accounting-oracle.mjs'], + ignorePatterns: [ + '.agents/**', + '.claude/**', + '.clawpatch/**', + '.codevetter/**', + '.codex/**', + '.impeccable/**', + '.symphony/**', + '**/.build/**', + '**/dist/**', + '**/node_modules/**', + '**/target/**', + 'artifacts/**', + ], + testRunner: 'command', + commandRunner: { + command: 'node --test scripts/qualify-codex-accounting-oracle.test.mjs', + }, + coverageAnalysis: 'off', + concurrency: 1, + timeoutMS: 30_000, + reporters: ['clear-text', 'json'], + jsonReporter: { + fileName: 'artifacts/tooling/stryker/accounting-mutation-report.json', + }, + thresholds: { + high: 80, + low: 60, + break: 80, + }, +}; From 0d041e434c071efcf9431221fe32b7ca62a91af6 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Mon, 31 Aug 2026 03:19:23 +0530 Subject: [PATCH 12/37] feat: add bounded external collector receipts --- PROJECT_STATUS.md | 25 +- apps/desktop/src-tauri/src/bin/codevetter.rs | 156 ++++ apps/desktop/src-tauri/src/commands/mod.rs | 1 + .../src-tauri/src/commands/tool_collectors.rs | 843 ++++++++++++++++++ docs/architecture/review-pipeline.md | 19 +- docs/knowledge/tooling-sandboxing.md | 53 +- .../tooling-secrets-and-supply-chain.md | 85 +- evidence/security/osv-baseline-2026-08-31.md | 15 + ...pple-container-qualification-2026-08-31.md | 60 ++ .../tool-collector-foundation-2026-08-31.md | 60 ++ 10 files changed, 1294 insertions(+), 23 deletions(-) create mode 100644 apps/desktop/src-tauri/src/commands/tool_collectors.rs create mode 100644 evidence/verification/apple-container-qualification-2026-08-31.md create mode 100644 evidence/verification/tool-collector-foundation-2026-08-31.md diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index a38d40a9..fe784f9b 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -1,6 +1,6 @@ # Project Status -Last updated: 2026-08-24 +Last updated: 2026-08-31 ## Why / What @@ -39,7 +39,6 @@ separately approved safety design justify renewed investment. External: - Bundled `ccusage` 20.0.20 sidecar — local, offline Claude/Codex/Grok usage accounting; exact updates are opened weekly and remain qualification-gated. -- User-supplied LLM API keys (Anthropic / OpenAI / OpenRouter) stored in user settings — no server-side auth. - Installed and authenticated Codex or Claude CLI for Work conversations; provider account policy remains external to CodeVetter. - GitHub Releases + GitHub Actions — `auto-release.yml` cuts a `v` release on `tauri.conf.json` version bumps, dispatching `release.yml` to build/sign/upload Tauri binaries; `@tauri-apps/plugin-updater` consumes the `latest.json` manifest. - Cloudflare Pages — hosts the landing page (`codevetter` project, codevetter.com). @@ -56,6 +55,28 @@ Internal (fleet): ## Timeline +- **2026-08-31 — Bounded external collector foundation (unreleased source):** + Added `codevetter collect` and a shared Rust `codevetter.tool-collection/v1` + receipt for one exact clean checked-out Git range. The first adapter executes + an exact Gitleaks 8.30.1 bundle sibling or explicit debug/test override without a + shell, with a minimal environment, timeout, bounded output, binary/config + identity, repository-relative normalization, and raw secret fields removed + before serialization. cargo-audit and cargo-llvm-cov currently return + explicit `unavailable` evidence after optional exact-version preflight; they + do not install tools, fetch advisory data, or claim coverage. No sidecar is + yet bundled and no release claim is made; packaging and the remaining + collectors stay tracked in issue #198. + +- **2026-08-31 — Apple Container trial qualified with a containment caveat:** + Installed the signed/notarized 1.3.1 CLI after owner authorization and + exercised a 1-CPU/256-MB, internal-network, no-DNS, read-only-root sandbox on + the supported arm64 macOS 27 host. The cached no-op run took 0.61 seconds and + teardown left zero containers. A controlled traversal fixture proved the CLI + does not enforce CodeVetter's workspace-root boundary, so any adapter must + canonicalize and reject out-of-root mounts itself. This is external-prerequisite + qualification, not a bundled dependency or architecture approval; issue #197 + remains open. + - **2026-08-24 — Unified local change check (unreleased source):** the packaged `codevetter` CLI now accepts one clean checked-out PR head or Git range plus task intent and emits `codevetter.local-check/v1`. The runner resolves exact diff --git a/apps/desktop/src-tauri/src/bin/codevetter.rs b/apps/desktop/src-tauri/src/bin/codevetter.rs index 7df6939e..bc23d5bb 100644 --- a/apps/desktop/src-tauri/src/bin/codevetter.rs +++ b/apps/desktop/src-tauri/src/bin/codevetter.rs @@ -3,6 +3,10 @@ use codevetter_desktop::commands::local_check::{ LocalCheckPreflightReceipt, LocalCheckReceipt, LocalCheckStatus, LocalCheckTarget, LocalCheckVerdict, }; +use codevetter_desktop::commands::tool_collectors::{ + collect_tool_evidence, CollectorKind, CollectorStatus, ToolCollectionInput, + ToolCollectionReceipt, +}; use codevetter_desktop::commands::trex_preview::{ execute_trex_preview, TrexChangeKind, TrexPreviewReceipt, TrexPreviewRunInput, TrexPreviewVerdict, @@ -17,6 +21,7 @@ CodeVetter execution-backed verification Usage: codevetter check (--pr | --range ) --task [options] codevetter trex (--pr | --range ) --preview [--repo ] [--json] + codevetter collect --range --collector [--collector ...] [--repo ] [--json] codevetter --version Options: @@ -39,6 +44,7 @@ Options: --samples Performance samples, 2-10 (default: 3) --warmups Performance warmups, 0-5 (default: 1) --timeout-ms Per-workload timeout, 100-120000 (default: 30000) + --collector gitleaks, cargo-audit, or cargo-llvm-cov (repeatable) --json Print only the canonical receipt JSON "; @@ -75,8 +81,17 @@ struct CheckArguments { output: OutputMode, } +#[derive(Debug, Clone, PartialEq, Eq)] +struct CollectArguments { + repo_path: PathBuf, + change: String, + collectors: Vec, + output: OutputMode, +} + enum CliCommand { Check(Box), + Collect(CollectArguments), Trex(TrexArguments), Help, Version, @@ -106,10 +121,29 @@ async fn run() -> Result { Ok(0) } CliCommand::Check(arguments) => run_check(*arguments).await, + CliCommand::Collect(arguments) => run_collect(arguments).await, CliCommand::Trex(arguments) => run_trex(arguments).await, } } +async fn run_collect(arguments: CollectArguments) -> Result { + let receipt = collect_tool_evidence(ToolCollectionInput { + repo_path: arguments.repo_path, + change: arguments.change, + collectors: arguments.collectors, + }) + .await?; + match arguments.output { + OutputMode::Json => println!( + "{}", + serde_json::to_string(&receipt) + .map_err(|error| format!("serialize tool collection receipt: {error}"))? + ), + OutputMode::Human => print!("{}", render_human_collection(&receipt)), + } + Ok(collection_exit_code(&receipt)) +} + async fn run_check(arguments: CheckArguments) -> Result { let output = arguments.output; let preflight = arguments.preflight; @@ -212,6 +246,7 @@ fn parse_arguments( "--help" | "-h" | "help" => return Ok(CliCommand::Help), "--version" | "-V" => return Ok(CliCommand::Version), "check" => return parse_check(arguments, cwd), + "collect" => return parse_collect(arguments, cwd), "trex" => {} _ => return Err(format!("unknown command `{command}`\n\n{HELP}")), } @@ -257,6 +292,38 @@ fn parse_arguments( })) } +fn parse_collect( + mut arguments: impl Iterator, + cwd: &Path, +) -> Result { + let mut repo_path = None; + let mut range = None; + let mut collectors = Vec::new(); + let mut output = OutputMode::Human; + while let Some(argument) = arguments.next() { + match argument.as_str() { + "--repo" => repo_path = Some(PathBuf::from(required_value(&mut arguments, "--repo")?)), + "--range" => range = Some(required_value(&mut arguments, "--range")?), + "--collector" => collectors.push(CollectorKind::parse(&required_value( + &mut arguments, + "--collector", + )?)?), + "--json" => output = OutputMode::Json, + "--help" | "-h" => return Ok(CliCommand::Help), + _ => return Err(format!("unknown collect argument `{argument}`")), + } + } + if collectors.is_empty() { + return Err("at least one --collector is required".into()); + } + Ok(CliCommand::Collect(CollectArguments { + repo_path: repo_path.unwrap_or_else(|| cwd.to_path_buf()), + change: range.ok_or_else(|| "--range is required".to_string())?, + collectors, + output, + })) +} + fn parse_check( mut arguments: impl Iterator, cwd: &Path, @@ -451,6 +518,50 @@ fn preflight_exit_code(status: LocalCheckStatus) -> i32 { } } +fn collection_exit_code(receipt: &ToolCollectionReceipt) -> i32 { + if receipt.collectors.iter().any(|collector| { + matches!( + collector.status, + CollectorStatus::Unavailable | CollectorStatus::Error + ) + }) { + 2 + } else if receipt + .collectors + .iter() + .any(|collector| collector.status == CollectorStatus::Findings) + { + 1 + } else { + 0 + } +} + +fn render_human_collection(receipt: &ToolCollectionReceipt) -> String { + let mut output = format!("head: {}\n", receipt.source.head_sha); + for collector in &receipt.collectors { + let name = serde_json::to_value(collector.collector) + .ok() + .and_then(|value| value.as_str().map(ToOwned::to_owned)) + .unwrap_or_else(|| "unknown".into()); + let status = serde_json::to_value(collector.status) + .ok() + .and_then(|value| value.as_str().map(ToOwned::to_owned)) + .unwrap_or_else(|| "unknown".into()); + output.push_str(&format!( + "{name}: {status} ({} finding(s), {} ms)\n", + collector.finding_count, collector.duration_ms + )); + } + if !receipt.limitations.is_empty() { + output.push_str("limitations:\n"); + for limitation in &receipt.limitations { + output.push_str(&format!("- {limitation}\n")); + } + } + output +} + fn render_human_preflight(receipt: &LocalCheckPreflightReceipt) -> String { let target = |value: Option<&LocalCheckTarget>| { value @@ -919,6 +1030,51 @@ mod tests { .is_err()); } + #[test] + fn collect_parser_requires_a_range_and_explicit_supported_collectors() { + let cwd = Path::new("/tmp/widget"); + let CliCommand::Collect(arguments) = parse_arguments( + [ + "collect".into(), + "--range".into(), + "main..HEAD".into(), + "--collector".into(), + "gitleaks".into(), + "--collector".into(), + "cargo-audit".into(), + "--json".into(), + ], + cwd, + ) + .expect("collect arguments") else { + panic!("expected collect command") + }; + assert_eq!(arguments.repo_path, cwd); + assert_eq!(arguments.change, "main..HEAD"); + assert_eq!( + arguments.collectors, + vec![CollectorKind::Gitleaks, CollectorKind::CargoAudit] + ); + assert_eq!(arguments.output, OutputMode::Json); + + assert!(parse_arguments( + ["collect".into(), "--range".into(), "main..HEAD".into()], + cwd, + ) + .is_err()); + assert!(parse_arguments( + [ + "collect".into(), + "--range".into(), + "main..HEAD".into(), + "--collector".into(), + "unknown".into(), + ], + cwd, + ) + .is_err()); + } + #[test] fn output_and_exit_codes_preserve_receipt_meaning() { let config: serde_json::Value = diff --git a/apps/desktop/src-tauri/src/commands/mod.rs b/apps/desktop/src-tauri/src/commands/mod.rs index 76a5152a..b2095fe4 100644 --- a/apps/desktop/src-tauri/src/commands/mod.rs +++ b/apps/desktop/src-tauri/src/commands/mod.rs @@ -53,6 +53,7 @@ pub mod spec_coverage; pub mod structural_graph; pub mod synthetic_qa; pub mod taste; +pub mod tool_collectors; pub mod trex_preview; pub mod trex_watcher; pub mod unpack; diff --git a/apps/desktop/src-tauri/src/commands/tool_collectors.rs b/apps/desktop/src-tauri/src/commands/tool_collectors.rs new file mode 100644 index 00000000..fe2a96de --- /dev/null +++ b/apps/desktop/src-tauri/src/commands/tool_collectors.rs @@ -0,0 +1,843 @@ +//! Local, bounded adapters for external verification collectors. +//! +//! Collectors remain subordinate evidence. This module owns source identity, +//! process limits, redaction, and the normalized receipt; a tool exit code does +//! not become a CodeVetter verdict. + +use std::collections::BTreeSet; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::process::Stdio; +use std::time::{Duration, Instant}; + +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; +use tokio::io::{AsyncRead, AsyncReadExt}; +use tokio::process::Command; + +use super::trex_preview::{resolve_scope_change, TrexSourceReceipt}; + +const GITLEAKS_VERSION: &str = "8.30.1"; +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(120); +const MAX_STDOUT_BYTES: usize = 256 * 1024; +const MAX_STDERR_BYTES: usize = 256 * 1024; +const MAX_REPORT_BYTES: usize = 8 * 1024 * 1024; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "kebab-case")] +pub enum CollectorKind { + Gitleaks, + CargoAudit, + CargoLlvmCov, +} + +impl CollectorKind { + pub fn parse(value: &str) -> Result { + match value { + "gitleaks" => Ok(Self::Gitleaks), + "cargo-audit" => Ok(Self::CargoAudit), + "cargo-llvm-cov" => Ok(Self::CargoLlvmCov), + _ => Err(format!( + "unsupported collector `{value}`; expected gitleaks, cargo-audit, or cargo-llvm-cov" + )), + } + } + + fn binary_name(self) -> &'static str { + match self { + Self::Gitleaks => "gitleaks", + Self::CargoAudit => "cargo-audit", + Self::CargoLlvmCov => "cargo-llvm-cov", + } + } + + fn expected_version(self) -> &'static str { + match self { + Self::Gitleaks => GITLEAKS_VERSION, + Self::CargoAudit => "0.22.2", + Self::CargoLlvmCov => "0.9.0", + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum CollectorStatus { + Clean, + Findings, + Unavailable, + Error, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct CollectorToolIdentity { + pub name: String, + pub version: String, + pub source: String, + pub sha256: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CollectorResult { + pub collector: CollectorKind, + pub status: CollectorStatus, + pub duration_ms: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool: Option, + pub finding_count: usize, + pub evidence: Value, + pub limitations: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolCollectionReceipt { + pub schema_version: String, + pub ran_at: String, + pub repo_path: String, + pub source: TrexSourceReceipt, + pub collectors: Vec, + pub limitations: Vec, +} + +#[derive(Debug, Clone)] +pub struct ToolCollectionInput { + pub repo_path: PathBuf, + pub change: String, + pub collectors: Vec, +} + +#[derive(Debug, Clone, Default)] +struct ToolPaths { + gitleaks: Option, + cargo_audit: Option, + cargo_llvm_cov: Option, +} + +#[derive(Debug, Clone)] +struct ProductBinary { + path: PathBuf, + source: &'static str, +} + +impl ToolPaths { + fn product() -> Self { + Self { + gitleaks: resolve_product_binary( + "CODEVETTER_GITLEAKS_BIN", + CollectorKind::Gitleaks.binary_name(), + ), + cargo_audit: resolve_product_binary( + "CODEVETTER_CARGO_AUDIT_BIN", + CollectorKind::CargoAudit.binary_name(), + ), + cargo_llvm_cov: resolve_product_binary( + "CODEVETTER_CARGO_LLVM_COV_BIN", + CollectorKind::CargoLlvmCov.binary_name(), + ), + } + } + + fn get(&self, kind: CollectorKind) -> Option<&ProductBinary> { + match kind { + CollectorKind::Gitleaks => self.gitleaks.as_ref(), + CollectorKind::CargoAudit => self.cargo_audit.as_ref(), + CollectorKind::CargoLlvmCov => self.cargo_llvm_cov.as_ref(), + } + } +} + +pub async fn collect_tool_evidence( + input: ToolCollectionInput, +) -> Result { + collect_tool_evidence_with_paths(input, ToolPaths::product()).await +} + +async fn collect_tool_evidence_with_paths( + input: ToolCollectionInput, + paths: ToolPaths, +) -> Result { + if input.collectors.is_empty() { + return Err("At least one collector is required".into()); + } + let repo = canonical_clean_repository(&input.repo_path)?; + let repo_text = repo.to_string_lossy().into_owned(); + let source = resolve_scope_change(&repo_text, &input.change).await?; + require_checked_out_head(&repo, &source.head_sha)?; + let selected = input.collectors.into_iter().collect::>(); + let mut collectors = Vec::with_capacity(selected.len()); + for kind in selected { + let started = Instant::now(); + let result = match (kind, paths.get(kind)) { + (CollectorKind::Gitleaks, Some(binary)) => { + run_gitleaks(binary, &repo, &source, started).await + } + (_, Some(binary)) => preflight_only(kind, binary, started).await, + (_, None) => unavailable( + kind, + started, + format!( + "The pinned {} {} product binary is not bundled or explicitly provided", + kind.binary_name(), + kind.expected_version() + ), + ), + }; + collectors.push(result); + } + let limitations = collectors + .iter() + .filter(|result| { + matches!( + result.status, + CollectorStatus::Unavailable | CollectorStatus::Error + ) + }) + .flat_map(|result| result.limitations.iter().cloned()) + .collect(); + Ok(ToolCollectionReceipt { + schema_version: "codevetter.tool-collection/v1".into(), + ran_at: chrono::Utc::now().to_rfc3339(), + repo_path: repo_text, + source, + collectors, + limitations, + }) +} + +async fn run_gitleaks( + binary: &ProductBinary, + repo: &Path, + source: &TrexSourceReceipt, + started: Instant, +) -> CollectorResult { + let identity = match tool_identity(CollectorKind::Gitleaks, binary).await { + Ok(identity) => identity, + Err(error) => return error_result(CollectorKind::Gitleaks, started, error), + }; + let range = format!("{}..{}", source.base_sha, source.head_sha); + let mut command = Command::new(&binary.path); + command + .args([ + "git", + "--no-banner", + "--no-color", + "--redact=100", + "--report-format", + "json", + "--report-path", + "-", + ]) + .arg("--log-opts") + .arg(&range) + .arg(repo) + .current_dir(repo) + .env_clear() + .env("PATH", "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin") + .env("NO_COLOR", "1") + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .kill_on_drop(true); + + let execution = + execute_bounded(command, DEFAULT_TIMEOUT, MAX_REPORT_BYTES, MAX_STDERR_BYTES).await; + let result = match execution { + Err(error) => error_result(CollectorKind::Gitleaks, started, error), + Ok(output) if !matches!(output.code, 0 | 1) => error_result( + CollectorKind::Gitleaks, + started, + format!( + "gitleaks exited with {}: {}", + output.code, + safe_diagnostic(&output.stderr) + ), + ), + Ok(output) => match read_gitleaks_report(&output.stdout) + .and_then(|findings| validate_gitleaks_attribution(findings, source)) + { + Ok(findings) => { + let finding_count = findings.len(); + let status = if finding_count == 0 { + CollectorStatus::Clean + } else { + CollectorStatus::Findings + }; + CollectorResult { + collector: CollectorKind::Gitleaks, + status, + duration_ms: elapsed_ms(started), + tool: Some(identity), + finding_count, + evidence: json!({ + "range": range, + "configuration": gitleaks_configuration_identity(repo), + "redaction": "secret and match values dropped before normalization", + "findings": findings, + "process_exit_code": output.code, + }), + limitations: Vec::new(), + } + } + Err(error) => error_result(CollectorKind::Gitleaks, started, error), + }, + }; + result +} + +async fn preflight_only( + kind: CollectorKind, + binary: &ProductBinary, + started: Instant, +) -> CollectorResult { + match tool_identity(kind, binary).await { + Ok(identity) => CollectorResult { + collector: kind, + status: CollectorStatus::Unavailable, + duration_ms: elapsed_ms(started), + tool: Some(identity), + finding_count: 0, + evidence: json!({"preflight": "binary identity verified"}), + limitations: vec![format!( + "{} execution remains claim-closed until its offline data/toolchain prerequisite is packaged and qualified", + kind.binary_name() + )], + }, + Err(error) => error_result(kind, started, error), + } +} + +async fn tool_identity( + kind: CollectorKind, + binary: &ProductBinary, +) -> Result { + if !binary.path.is_file() { + return Err(format!("{} binary is missing", kind.binary_name())); + } + let argument = match kind { + CollectorKind::Gitleaks => "version", + CollectorKind::CargoAudit | CollectorKind::CargoLlvmCov => "--version", + }; + let mut command = Command::new(&binary.path); + command + .arg(argument) + .env_clear() + .env("PATH", "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin") + .env("NO_COLOR", "1") + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .kill_on_drop(true); + let output = execute_bounded( + command, + Duration::from_secs(10), + MAX_STDOUT_BYTES, + MAX_STDERR_BYTES, + ) + .await?; + if output.code != 0 { + return Err(format!( + "{} version probe exited with {}", + kind.binary_name(), + output.code + )); + } + let version = String::from_utf8(output.stdout) + .map_err(|_| format!("{} returned a non-UTF-8 version", kind.binary_name()))?; + if !version + .split_ascii_whitespace() + .any(|token| token.trim_start_matches('v') == kind.expected_version()) + { + return Err(format!( + "{} version mismatch: expected {}, received {}", + kind.binary_name(), + kind.expected_version(), + safe_diagnostic(version.as_bytes()) + )); + } + Ok(CollectorToolIdentity { + name: kind.binary_name().into(), + version: kind.expected_version().into(), + source: binary.source.into(), + sha256: sha256_file(&binary.path)?, + }) +} + +fn sha256_file(path: &Path) -> Result { + let mut file = std::fs::File::open(path) + .map_err(|error| format!("open {} binary for identity: {error}", path.display()))?; + let mut digest = Sha256::new(); + let mut buffer = [0_u8; 64 * 1024]; + loop { + let read = file + .read(&mut buffer) + .map_err(|error| format!("hash {} binary: {error}", path.display()))?; + if read == 0 { + break; + } + digest.update(&buffer[..read]); + } + Ok(format!("{:x}", digest.finalize())) +} + +#[derive(Debug)] +struct ProcessOutput { + code: i32, + stdout: Vec, + stderr: Vec, +} + +async fn execute_bounded( + mut command: Command, + timeout: Duration, + stdout_limit: usize, + stderr_limit: usize, +) -> Result { + let mut child = command + .spawn() + .map_err(|error| format!("launch collector: {error}"))?; + let stdout = child + .stdout + .take() + .ok_or_else(|| "collector stdout was unavailable".to_string())?; + let stderr = child + .stderr + .take() + .ok_or_else(|| "collector stderr was unavailable".to_string())?; + let execution = tokio::time::timeout(timeout, async { + let (status, stdout, stderr) = tokio::join!( + child.wait(), + read_capped(stdout, stdout_limit), + read_capped(stderr, stderr_limit) + ); + (status, stdout, stderr) + }) + .await; + let (status, stdout, stderr) = match execution { + Ok(output) => output, + Err(_) => { + let _ = child.kill().await; + return Err(format!( + "collector exceeded the {} second limit", + timeout.as_secs() + )); + } + }; + let status = status.map_err(|error| format!("wait for collector: {error}"))?; + let (stdout, stdout_exceeded) = + stdout.map_err(|error| format!("read collector stdout: {error}"))?; + let (stderr, stderr_exceeded) = + stderr.map_err(|error| format!("read collector stderr: {error}"))?; + if stdout_exceeded || stderr_exceeded { + return Err("collector output exceeded the bounded evidence limit".into()); + } + Ok(ProcessOutput { + code: status.code().unwrap_or(-1), + stdout, + stderr, + }) +} + +async fn read_capped( + mut reader: R, + limit: usize, +) -> std::io::Result<(Vec, bool)> { + let mut output = Vec::with_capacity(limit.min(64 * 1024)); + let mut exceeded = false; + let mut buffer = [0_u8; 8192]; + loop { + let read = reader.read(&mut buffer).await?; + if read == 0 { + break; + } + let remaining = limit.saturating_sub(output.len()); + output.extend_from_slice(&buffer[..read.min(remaining)]); + exceeded |= read > remaining; + } + Ok((output, exceeded)) +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct RawGitleaksFinding { + #[serde(rename = "RuleID")] + rule_id: String, + #[serde(default)] + description: String, + file: String, + #[serde(default)] + start_line: u64, + #[serde(default)] + end_line: u64, + #[serde(default)] + commit: String, + #[serde(default)] + fingerprint: String, +} + +#[derive(Debug, Serialize)] +struct GitleaksFinding { + rule_id: String, + description: String, + file: String, + start_line: u64, + end_line: u64, + commit: String, + fingerprint: String, +} + +fn read_gitleaks_report(bytes: &[u8]) -> Result, String> { + if bytes.len() > MAX_REPORT_BYTES { + return Err("gitleaks report is oversized".into()); + } + let raw_findings: Vec = + serde_json::from_slice(bytes).map_err(|error| format!("parse gitleaks report: {error}"))?; + let mut findings = raw_findings + .into_iter() + .map(|finding| { + Ok(GitleaksFinding { + rule_id: safe_text(&finding.rule_id, 120), + description: safe_text(&finding.description, 240), + file: normalize_relative_path(&finding.file)?, + start_line: finding.start_line, + end_line: finding.end_line, + commit: safe_hex(&finding.commit, 40), + fingerprint: safe_text(&finding.fingerprint, 240), + }) + }) + .collect::, String>>()?; + findings.sort_by(|left, right| { + (&left.file, left.start_line, &left.rule_id).cmp(&( + &right.file, + right.start_line, + &right.rule_id, + )) + }); + Ok(findings) +} + +fn validate_gitleaks_attribution( + findings: Vec, + source: &TrexSourceReceipt, +) -> Result, String> { + let commits = source + .commits + .iter() + .map(String::as_str) + .collect::>(); + let changed_paths = source + .changed_paths + .iter() + .map(String::as_str) + .collect::>(); + if findings.iter().any(|finding| { + !commits.contains(finding.commit.as_str()) || !changed_paths.contains(finding.file.as_str()) + }) { + return Err("gitleaks returned a finding outside the resolved change".into()); + } + Ok(findings) +} + +fn gitleaks_configuration_identity(repo: &Path) -> Value { + for name in [".gitleaks.toml", "gitleaks.toml"] { + let path = repo.join(name); + if let Ok(bytes) = std::fs::read(&path) { + return json!({ + "source": name, + "sha256": format!("{:x}", Sha256::digest(bytes)), + }); + } + } + json!({"source": "gitleaks-8.30.1-embedded-default"}) +} + +fn normalize_relative_path(value: &str) -> Result { + let normalized = value.replace('\\', "/"); + let path = Path::new(&normalized); + if path.is_absolute() + || path + .components() + .any(|part| matches!(part, std::path::Component::ParentDir)) + { + return Err("gitleaks returned a non-contained finding path".into()); + } + Ok(normalized) +} + +fn canonical_clean_repository(path: &Path) -> Result { + let canonical = path + .canonicalize() + .map_err(|error| format!("repository {} is unavailable: {error}", path.display()))?; + if !canonical.is_dir() { + return Err("Tool collection requires a Git repository root".into()); + } + let reported_root = git_text(&canonical, &["rev-parse", "--show-toplevel"])?; + let reported_root = PathBuf::from(reported_root) + .canonicalize() + .map_err(|error| format!("canonicalize Git repository root: {error}"))?; + if reported_root != canonical { + return Err("Tool collection requires the Git repository root, not a subdirectory".into()); + } + if !git_text( + &canonical, + &["status", "--porcelain=v1", "--untracked-files=normal"], + )? + .is_empty() + { + return Err( + "Tool collection requires a clean checkout so evidence maps to one immutable source" + .into(), + ); + } + Ok(canonical) +} + +fn require_checked_out_head(repo: &Path, expected: &str) -> Result<(), String> { + let head = git_text(repo, &["rev-parse", "HEAD"])?; + if head != expected { + return Err("Resolved change head is not the checked-out repository HEAD".into()); + } + Ok(()) +} + +fn git_text(repo: &Path, arguments: &[&str]) -> Result { + let output = std::process::Command::new("git") + .args(arguments) + .current_dir(repo) + .stdin(Stdio::null()) + .output() + .map_err(|error| format!("Could not run Git: {error}"))?; + if !output.status.success() { + return Err(format!( + "Git could not inspect the local checkout: {}", + safe_diagnostic(&output.stderr) + )); + } + String::from_utf8(output.stdout) + .map(|value| value.trim().to_string()) + .map_err(|_| "Git returned non-UTF-8 evidence".into()) +} + +fn resolve_product_binary(variable: &str, name: &str) -> Option { + #[cfg(any(test, debug_assertions))] + { + if let Some(path) = std::env::var_os(variable).map(PathBuf::from) { + if path.is_file() { + return Some(ProductBinary { + path, + source: "explicit_debug_override", + }); + } + } + } + #[cfg(not(any(test, debug_assertions)))] + let _ = variable; + std::env::current_exe() + .ok() + .and_then(|executable| executable.parent().map(|parent| parent.join(name))) + .filter(|path| path.is_file()) + .map(|path| ProductBinary { + path, + source: "application_bundle_sibling", + }) +} + +fn unavailable(kind: CollectorKind, started: Instant, reason: String) -> CollectorResult { + CollectorResult { + collector: kind, + status: CollectorStatus::Unavailable, + duration_ms: elapsed_ms(started), + tool: None, + finding_count: 0, + evidence: json!({"preflight": "unavailable"}), + limitations: vec![reason], + } +} + +fn error_result(kind: CollectorKind, started: Instant, reason: String) -> CollectorResult { + CollectorResult { + collector: kind, + status: CollectorStatus::Error, + duration_ms: elapsed_ms(started), + tool: None, + finding_count: 0, + evidence: json!({"error_category": "collector_execution"}), + limitations: vec![safe_text(&reason, 500)], + } +} + +fn safe_diagnostic(bytes: &[u8]) -> String { + safe_text(&String::from_utf8_lossy(bytes), 500) +} + +fn safe_text(value: &str, limit: usize) -> String { + value + .chars() + .filter_map(|character| match character { + '\n' | '\r' | '\t' => Some(' '), + value if value.is_control() => None, + value => Some(value), + }) + .take(limit) + .collect::() + .split_whitespace() + .collect::>() + .join(" ") +} + +fn safe_hex(value: &str, limit: usize) -> String { + value + .chars() + .filter(|character| character.is_ascii_hexdigit()) + .take(limit) + .collect() +} + +fn elapsed_ms(started: Instant) -> u64 { + started.elapsed().as_millis().try_into().unwrap_or(u64::MAX) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::os::unix::fs::PermissionsExt; + use tempfile::TempDir; + + fn executable(path: &Path, contents: &str) { + std::fs::write(path, contents).expect("write fixture executable"); + let mut permissions = std::fs::metadata(path) + .expect("fixture metadata") + .permissions(); + permissions.set_mode(0o755); + std::fs::set_permissions(path, permissions).expect("fixture permissions"); + } + + fn repository() -> (TempDir, String, String) { + let directory = tempfile::tempdir().expect("temp repository"); + let repo = directory.path(); + for args in [ + vec!["init"], + vec!["config", "user.email", "fixture@example.com"], + vec!["config", "user.name", "Fixture"], + ] { + assert!(std::process::Command::new("git") + .args(args) + .current_dir(repo) + .status() + .expect("git") + .success()); + } + std::fs::write(repo.join("README.md"), "base\n").expect("base file"); + assert!(std::process::Command::new("git") + .args(["add", "README.md"]) + .current_dir(repo) + .status() + .expect("git add") + .success()); + assert!(std::process::Command::new("git") + .args(["commit", "-m", "base"]) + .current_dir(repo) + .status() + .expect("git commit") + .success()); + let base = git_text(repo, &["rev-parse", "HEAD"]).expect("base sha"); + std::fs::write(repo.join("README.md"), "base\nchange\n").expect("changed file"); + assert!(std::process::Command::new("git") + .args(["add", "README.md"]) + .current_dir(repo) + .status() + .expect("git add") + .success()); + assert!(std::process::Command::new("git") + .args(["commit", "-m", "change"]) + .current_dir(repo) + .status() + .expect("git commit") + .success()); + let head = git_text(repo, &["rev-parse", "HEAD"]).expect("head sha"); + (directory, base, head) + } + + #[tokio::test] + async fn gitleaks_receipt_drops_raw_secret_fields() { + let (repo, base, head) = repository(); + let tools = tempfile::tempdir().expect("tool directory"); + let binary = tools.path().join("gitleaks"); + executable( + &binary, + r##"#!/bin/sh +if [ "$1" = "version" ]; then printf '8.30.1\n'; exit 0; fi +printf '[{"RuleID":"fixture-token","Description":"fixture","File":"README.md","StartLine":2,"EndLine":2,"Commit":"HEAD_SHA","Fingerprint":"safe-fingerprint","Secret":"must-not-survive","Match":"token=must-not-survive"}]' +exit 1 +"##, + ); + let script = std::fs::read_to_string(&binary) + .expect("read fixture executable") + .replace("HEAD_SHA", &head); + executable(&binary, &script); + let receipt = collect_tool_evidence_with_paths( + ToolCollectionInput { + repo_path: repo.path().into(), + change: format!("{base}..{head}"), + collectors: vec![CollectorKind::Gitleaks], + }, + ToolPaths { + gitleaks: Some(ProductBinary { + path: binary, + source: "test_fixture", + }), + ..ToolPaths::default() + }, + ) + .await + .expect("collector receipt"); + assert_eq!(receipt.collectors[0].status, CollectorStatus::Findings); + assert_eq!(receipt.collectors[0].finding_count, 1); + let serialized = serde_json::to_string(&receipt).expect("serialize receipt"); + assert!(!serialized.contains("must-not-survive")); + assert!(!serialized.contains("\"Secret\"")); + assert!(!serialized.contains("\"Match\"")); + } + + #[tokio::test] + async fn missing_product_tools_are_explicitly_unavailable() { + let (repo, base, head) = repository(); + let receipt = collect_tool_evidence_with_paths( + ToolCollectionInput { + repo_path: repo.path().into(), + change: format!("{base}..{head}"), + collectors: vec![CollectorKind::CargoAudit, CollectorKind::CargoLlvmCov], + }, + ToolPaths::default(), + ) + .await + .expect("collector receipt"); + assert!(receipt + .collectors + .iter() + .all(|collector| collector.status == CollectorStatus::Unavailable)); + assert_eq!(receipt.limitations.len(), 2); + } + + #[test] + fn gitleaks_findings_must_belong_to_the_resolved_change() { + let source = TrexSourceReceipt { + kind: super::super::trex_preview::TrexChangeKind::Range, + input: "base..head".into(), + base_sha: "a".repeat(40), + head_sha: "b".repeat(40), + commits: vec!["b".repeat(40)], + changed_paths: vec!["src/changed.rs".into()], + }; + let finding = GitleaksFinding { + rule_id: "fixture".into(), + description: "fixture".into(), + file: "src/outside.rs".into(), + start_line: 1, + end_line: 1, + commit: "c".repeat(40), + fingerprint: "fixture".into(), + }; + assert!(validate_gitleaks_attribution(vec![finding], &source).is_err()); + } +} diff --git a/docs/architecture/review-pipeline.md b/docs/architecture/review-pipeline.md index 664938a5..df37adba 100644 --- a/docs/architecture/review-pipeline.md +++ b/docs/architecture/review-pipeline.md @@ -97,6 +97,18 @@ removed without discarding otherwise valid evidence. Qualification diagnostics and rejected/stale/unresolved counts stay in the manifest so the UI cannot turn partial evidence into full confidence. +## External collector boundary + +The unreleased `codevetter collect` path resolves the same exact clean Git +change before invoking an optional local sidecar. `tool_collectors.rs` owns +binary/config identity, no-shell supervision, output limits, normalization, and +limitations; the external tool does not own the CodeVetter verdict. The first +implemented adapter is Gitleaks 8.30.1 and drops raw match/secret fields before +serialization. cargo-audit and cargo-llvm-cov remain claim-closed preflights +until their offline database and LLVM prerequisites are packaged. The +[qualification receipt](https://github.com/Codevetter/codevetter/blob/main/evidence/verification/tool-collector-foundation-2026-08-31.md) +records the proven slice; issue #198 owns packaging and remaining execution. + ## Manifest and interruption behavior SQLite stores additive run, unit, attempt, qualification, and checkpoint state. @@ -155,9 +167,10 @@ separately authorized. ## Standards packs `StandardsPack` (`review-service.ts`) groups checks by focus -(`product-safety`, `security-boundary`, …). The active pack is persisted in -user settings (`codevetter_review_config` localStorage key, mirrored to Tauri -preferences) and linked to reviews via `local_reviews.standards_pack`. The +(`product-safety`, `security-boundary`, …). The active pack is persisted in an +allowlisted `codevetter_review_config` localStorage record that cannot retain +legacy provider credentials, and linked to reviews via +`local_reviews.standards_pack`. The Rubrics page (`/rubrics`) handles pack authoring, exact prompt preview, per-pack usage stats, and cloning. diff --git a/docs/knowledge/tooling-sandboxing.md b/docs/knowledge/tooling-sandboxing.md index 597ce8f1..beee82d2 100644 --- a/docs/knowledge/tooling-sandboxing.md +++ b/docs/knowledge/tooling-sandboxing.md @@ -7,7 +7,7 @@ sidebar: # Sandboxed execution and static analysis -Verified **2026-08-30**. See [tooling-decisions.md](./tooling-decisions.md) for +Verified **2026-08-31**. See [tooling-decisions.md](./tooling-decisions.md) for the cross-category summary. Sandboxed execution is the mechanism CodeVetter's verdicts rest on: if the @@ -15,7 +15,27 @@ isolation is not reproducible, the evidence is not either. This page covers isolation on Apple Silicon, the determinism controls that make runs comparable, and static analyzers that emit SARIF. -## Recommended: `libkrun`, with a VZ upgrade path +## Recommended sequence: prove the contract with Apple `container`, then embed + +On a supported host, qualify the official Apple `container` CLI first. It gives +the team a maintained, OCI-compatible lightweight-VM implementation with which +to measure containment, mount policy, networking, resource bounds, startup, +and teardown before accepting an in-process VMM dependency. The signed 1.3.1 +package is now qualified on the Apple Silicon macOS 27 trial host. A cached +no-op container started in 0.61 seconds; read-only root/workspace, bounded CPU +and memory, an internal no-DNS network, dropped capabilities, host-environment +absence, and teardown all behaved as expected. The first image/init-image run +took 20.56 seconds and the cached images occupied 1.45 GB. + +One contract failed: the CLI accepted a controlled bind source containing `..` +when it resolved outside the intended fixture root. CodeVetter must canonicalize +and enforce workspace containment itself; Apple Container's mount validation is +not that policy. The [qualification receipt](https://github.com/Codevetter/codevetter/blob/main/evidence/verification/apple-container-qualification-2026-08-31.md) +records identities, measurements, teardown, and remaining gates. Issue #197 +keeps the architecture decision open. + +If the measured contract is sound, choose between consuming Apple's +Containerization Swift package through a sidecar and embedding `libkrun`. **`libkrun`** (Apache-2.0, `containers/libkrun`, 2,643★) is a small VMM **library** written in Rust and built on Apple's `Hypervisor.framework`. It is @@ -24,10 +44,11 @@ backend via its C API, boots a minimal Linux microVM per verification run, and tears it down after. That matches the local-first, no-server constraint without requiring the user to install and license a GUI application. -**`apple/containerization`** (Apache-2.0, Swift, 8.9k★) is the follow-on once a -Swift sidecar is acceptable. It claims **sub-second** boot and is the only -option here with **native VM state save/restore** — the actual warm-start -mechanism. It requires **macOS 26** and Apple Silicon. +**`apple/containerization`** (Apache-2.0, Swift, 8.9k★) is the library beneath +that first-party CLI and is the follow-on once a Swift sidecar is acceptable. +It claims **sub-second** boot and is the only option here with **native VM state +save/restore** — the actual warm-start mechanism. It requires **macOS 26** and +Apple Silicon. **Colima + Lima** (MIT / Apache-2.0) is the pragmatic fallback for pre-macOS-26 or Intel machines: a one-time Homebrew install rather than a bundled component. @@ -37,7 +58,8 @@ or Intel machines: a one-time Homebrew install rather than a bundled component. | Option | License | Daemon? | Bundleable? | macOS ARM? | |---|---|---|---|---| | **libkrun** | Apache-2.0 | No — in-process C API | Yes, as a Rust dependency | Yes, explicit HVF backend | -| **apple/containerization** | Apache-2.0 | No (the `container` CLI wrapping it does) | Via a Swift sidecar | Apple Silicon, **macOS 26 only** | +| Apple `container` CLI | Apache-2.0 | Yes, system service | External prerequisite | Apple Silicon, macOS 26+ | +| **apple/containerization** | Apache-2.0 | No at library level | Via a Swift sidecar | Apple Silicon, macOS 26+ | | **Colima + Lima** | MIT / Apache-2.0 | CLI-managed VM, no GUI daemon | Prerequisite only | Yes (`vz` driver, default since Lima v1.0) | | **Podman** | Apache-2.0 | `podman machine` VM | Prerequisite only | Yes | | **Docker Desktop** | Engine Apache-2.0; **Desktop app proprietary** | Yes, GUI app + background VM | No | Yes, but license-gated | @@ -99,13 +121,13 @@ give you this. | Tool | License | Offline | Native SARIF | Verdict | |---|---|---|---|---| -| **Biome** | Apache-2.0 | Yes | Present, fidelity **UNVERIFIED** | **Check first** — already the repo's linter | -| **ast-grep** | MIT | Yes | Present, fidelity **UNVERIFIED** | Rust-native, good stack fit | +| **Biome** | Apache-2.0 | Yes | Verified SARIF 2.1.0 | **Wired** for repository evidence | +| **ast-grep** | MIT | Yes | Envelope invalid in 0.45.2 | Structural matching is sound, but do not upload its native SARIF | | **Ruff** | MIT | Yes | Yes (`--output-format=sarif`) | Python scope only | | **Semgrep** | CLI is LGPL-2.1 | Yes | Yes | Engine fine; **rules are the problem** | | **Clippy** | Apache-2.0 | Yes | **No** | Needs `clippy-sarif` converter | | **ESLint** | MIT | Yes | No | Third-party formatter | -| **CodeQL** | Custom | Yes | Yes | 🚫 **Legally disqualified** | +| **CodeQL** | Custom | Yes | Yes | Repository-only; 🚫 customer-code product use | ### 🚫 CodeQL is a hard blocker for this product @@ -130,9 +152,12 @@ Running Semgrep against a user's own code is internal use and fine. area** — that needs counsel before shipping, and is not cleared by the engine's LGPL alone. -**Lowest-friction path: verify Biome's own SARIF fidelity first.** The repo -already runs Biome for `pnpm lint`, so if its SARIF output is adequate, no -second linter is needed at all. +Biome's SARIF path is now verified and wired for the public repository, so no +second generic linter is justified without a missing-rule case. A focused +ast-grep 0.45.2 trial found correct structural locations but an invalid SARIF +root: `version` contains the tool version rather than `2.1.0`, and `$schema` is +absent. The [tracked receipt](https://github.com/Codevetter/codevetter/blob/main/evidence/security/ast-grep-sarif-qualification-2026-08-31.md) +keeps this a measured rejection instead of an assumed capability. ## Observability — do not add a dependency @@ -162,7 +187,5 @@ Flagged UNVERIFIED and worth closing before committing engineering time: before relying on it. - **`libkrun` cold-boot time on Apple Silicon.** "Smallest possible boot time" is a stated design goal; no published benchmark was found. -- **Biome and ast-grep SARIF fidelity.** Both have SARIF code in-repo; neither - was validated against real output. - **Podman's default macOS backend** (applehv vs libkrun vs QEMU) and its exact network/CPU/memory flags. diff --git a/docs/knowledge/tooling-secrets-and-supply-chain.md b/docs/knowledge/tooling-secrets-and-supply-chain.md index ebfbb8c4..a93b285e 100644 --- a/docs/knowledge/tooling-secrets-and-supply-chain.md +++ b/docs/knowledge/tooling-secrets-and-supply-chain.md @@ -36,10 +36,25 @@ the underlying `rustsec` crate is a **library**: it runs in-process in [RustSec/advisory-db](https://github.com/RustSec/advisory-db) at `~/.cargo/advisory-db` — trivially vendorable and shippable. - Pair with **cargo-deny** (`--offline`) for SPDX license-policy enforcement, - which nothing else here does for Rust. Note its output is structured JSON log - lines, **not SARIF** — the mapping would be yours to write. + which nothing else here does for Rust. Version 0.20.2 emits native SARIF + 2.1.0; the repository now verifies that output instead of relying on the + earlier, stale JSON-only assessment. -### `gitleaks` — bundle as a sidecar +### `cargo-deny` — wired repository policy + +The tracked `apps/desktop/src-tauri/deny.toml` evaluates the shipped Apple +Silicon target, permits only the license families present in the qualified +graph, denies wildcard requirements, denies unknown registries and all Git +dependencies, and reports duplicate versions without failing on Tauri-owned +convergence. `pnpm quality:rust-policy` runs the locked offline check locally. + +`repository-security.yml` downloads the exact 0.20.2 Linux musl binary, +verifies its publisher digest, uploads native license/source SARIF, and enforces +the bans lane separately. The qualified baseline has clean licenses and sources +plus 14 non-blocking duplicate-version warnings. Exact artifact identities and +limitations are in the [tracked evidence](https://github.com/Codevetter/codevetter/blob/main/evidence/security/cargo-deny-baseline-2026-08-31.md). + +### `gitleaks` — repository gate plus an unreleased product collector foundation **MIT**, and the core scanner's license is unchanged. Fully offline: no DB, no network, rules embedded or from `.gitleaks.toml`. Emits SARIF. Static Go binary, @@ -52,6 +67,60 @@ the commercial relicensing applies **only to `gitleaks/gitleaks-action` v2.0.0+* Caution: v8.30.1 shipped 2026-03-21 and recent commits are largely Dependabot. Not stale, but feature velocity has slowed. +The repository integration invokes the binary directly rather than the +commercially relicensed action: + +- `.husky/pre-commit` scans staged changes when Gitleaks is installed; +- `.husky/pre-push` scans complete Git history, with the previous pattern scan + retained only as a limited fallback; +- `pnpm quality:secrets` is the reproducible local command; +- `repository-security.yml` downloads v8.30.1, verifies its embedded SHA-256, + emits redacted SARIF, uploads the result, and fails on a finding. + +`.gitleaksignore` contains four exact historical fingerprints. It does not +allowlist whole paths or rules, so later findings in those files remain visible. +The unreleased Rust backend and `codevetter collect` CLI now add a bounded, +versioned `codevetter.tool-collection/v1` foundation. It resolves one clean +checked-out Git range, accepts only an exact 8.30.1 bundle sibling or explicit +debug/test override, records the binary SHA-256 and configuration identity, +invokes without a shell under time/output/environment bounds, and normalizes +only rule and repository-relative location metadata. Raw `Secret` and `Match` +fields are dropped before serialization, and the JSON report remains in a +bounded process pipe instead of a temporary file. Missing cargo-audit and cargo-llvm-cov +prerequisites remain explicit `unavailable` evidence. + +This source wiring is not evidence that any sidecar has shipped. No Gitleaks +binary is yet declared in Tauri resources or qualified inside a signed app +bundle, and cargo-audit/coverage execution remains unimplemented under issue +#198. + +### `osv-scanner` — offline repository runner wired, remediation required + +`pnpm quality:vulnerabilities` invokes the repository-owned 2.5.1 runner with +`--offline --offline-vulnerabilities`. It hashes the preseeded ecosystem +databases and writes SARIF plus a versioned receipt under ignored `artifacts/`. +Database refresh remains an explicit, separate network operation. The qualified +warm scan took 8.942 seconds and found 35 affected locked package versions and +52 advisory/package matches: 18 Rust packages and 17 docs-site npm packages. +The remainder is dominated by unmaintained GTK3 and Unicode Rust crates that +need platform/reachability classification rather than a blind allowlist. See +the [tracked baseline evidence](https://github.com/Codevetter/codevetter/blob/main/evidence/security/osv-baseline-2026-08-31.md). + +This explains why the root `pnpm audit` result was insufficient: it did not +cover the independent docs-site lockfile or RustSec. The trial is not yet a +gate because the current baseline would fail and each finding still needs +scope/reachability review. Integration and remediation are tracked in issue +#195. Database refresh must stay separate from the offline scan so a product +run cannot turn a transient network path into an implicit manifest upload. + +The approved Blume 1.5.3 and event-listener 5.4.2 maintenance reduced the same +offline result from 52 matches to 39 result instances (36 normalized rules) at +revision `855202998b56c1658b9decda22298a1b63fb5caf`. It did not make the graph +clean: the docs-site production audit still reports 12 high, 7 moderate, and 1 +low advisory in current transitive build/documentation paths. The +[baseline receipt](https://github.com/Codevetter/codevetter/blob/main/evidence/security/osv-baseline-2026-08-31.md) +keeps both the improvement and remaining exposure explicit. + ## Rejected ### TruffleHog — two independent blockers @@ -137,6 +206,16 @@ corrections to common belief: Useful detail: the misconfiguration checks bundle is **embedded in the binary at build time**, so that scanning survives with zero network. +A bounded 0.74.0 repository trial confirmed that +`--disable-telemetry --skip-version-check --skip-check-update` suppresses the +notification request and uses the embedded checks. It also showed why this is +not yet a useful maintained lane: the unbounded scan reported against a +dependency-owned Dockerfile, while the dependency-excluded scan misclassified a +warm-verification JSON fixture as CloudFormation and found no supported +first-party IaC surface. Trivy config scanning is therefore **trialled, not +wired** until such a surface exists. See the +[tracked receipt](https://github.com/Codevetter/codevetter/blob/main/evidence/security/trivy-config-qualification-2026-08-31.md). + ## SBOM formats Target **CycloneDX 1.7** (ECMA-424 2nd Edition, patch 1.7.1 2026-06-02) and diff --git a/evidence/security/osv-baseline-2026-08-31.md b/evidence/security/osv-baseline-2026-08-31.md index e361d2b8..de6cc638 100644 --- a/evidence/security/osv-baseline-2026-08-31.md +++ b/evidence/security/osv-baseline-2026-08-31.md @@ -53,3 +53,18 @@ Do not add a second vulnerability scanner or silently baseline these findings. First test the Blume update and bounded Cargo lockfile remediation, then repeat this exact offline scan. CI enforcement should follow remediation so the gate does not normalize known high-severity results. + +## Approved maintenance follow-up + +At exact revision `855202998b56c1658b9decda22298a1b63fb5caf`, after updating +Blume 1.0.4 to 1.5.3 and event-listener 5.4.1 to 5.4.2, the same database set +and offline runner completed in 9.745 seconds with 39 result instances and 36 +normalized SARIF rules. This is a reduction from 52 advisory/package matches, +not a clean result and not an allowlist. + +The independent docs-site production audit still reports 12 high, 7 moderate, +and 1 low advisory. Current Blume transitively retains those build/documentation +paths, and several have no resolution through the current direct version. The +repository-wide root production audit remains clean because the docs site has +its own lockfile; both facts must stay visible until issue #195 completes +reachability and upstream/remediation review. diff --git a/evidence/verification/apple-container-qualification-2026-08-31.md b/evidence/verification/apple-container-qualification-2026-08-31.md new file mode 100644 index 00000000..c947b003 --- /dev/null +++ b/evidence/verification/apple-container-qualification-2026-08-31.md @@ -0,0 +1,60 @@ +# Apple Container qualification — 2026-08-31 + +## Scope + +This receipt qualifies Apple's `container` CLI as an external-prerequisite +sandbox candidate on one supported development host. It does not add a product +dependency, bundle the runtime, or approve a release architecture. + +## Identity + +| Item | Observed identity | +|---|---| +| Host | macOS 27.0 build 26A5421a, arm64 | +| Installer | `container-1.3.1-installer-signed.pkg` | +| Publisher SHA-256 | `a7c1b9d7927d30875f2f6c7bd1d0cb06c2daa6ca57ce9e90a5144e898fdf54a8` | +| Signature | Developer ID Installer: Apple Inc. - Containerization (UPBK2H6LZM); trusted, notarized, timestamped | +| CLI | 1.3.1, release commit `a9a62e2` | +| API server | 1.3.1, release commit `a9a62e28f6beb88940122a3d7b286f2d5ae8053a` | +| Default kernel | Official Kata kernel 3.32.0 | +| Trial image | `alpine:3.22`, observed Alpine 3.22.5, local digest prefix `14358309a308` | + +The package digest matched the release publisher value before installation. +`pkgutil --check-signature` and `spctl` both accepted the downloaded package. +Installation required the owner's administrator authorization. The first +service start separately downloaded and verified the 664.3 MB default kernel. + +## Measured trial + +- First image/init-image run: 20.56 seconds wall time, including registry fetch + and unpack; the kernel had already been provisioned. +- Warm cached no-op run: 0.61 seconds wall time. +- Limits exercised: 1 CPU and 256 MB memory. +- Isolation exercised: an internal network plus `--no-dns`, read-only root, + read-only bind mount, and all Linux capabilities dropped. +- The controlled network probe failed, host-only environment marker was absent, + `/Users` and `/root/.ssh` were absent, and writes to both the mounted workspace + and `/root` failed with a read-only-filesystem error. +- `--rm` teardown left zero containers and zero local volumes. The cached image + and init image occupied 1.45 GB and were intentionally retained. + +The trial network was deleted after use. No host credential, home, SSH, cloud, +or production path was mounted or inspected. + +## Failed requirement: caller-owned path containment + +The CLI accepted a controlled bind source containing `..` when that source +resolved to an existing sibling fixture. Apple Container therefore provides +mount mechanics, but it does not enforce CodeVetter's workspace-root policy. +Any adapter must canonicalize both the allowed root and requested source, +reject sources outside the allowed root before process launch, pass the +canonical source to the CLI, and cover symlink and time-of-check/time-of-use +cases. The product must not treat the CLI's mount validation as containment. + +## Decision + +Keep Apple Container as the measured external-prerequisite candidate, not a +bundled dependency. Its isolation controls and warm-start result are promising, +but path containment must be owned by CodeVetter. Issue #197 remains open for +the CLI-versus-Containerization-versus-libkrun architecture comparison, idle +resource measurement, signing/notarization analysis, and an adapter design. diff --git a/evidence/verification/tool-collector-foundation-2026-08-31.md b/evidence/verification/tool-collector-foundation-2026-08-31.md new file mode 100644 index 00000000..b6c0ff9d --- /dev/null +++ b/evidence/verification/tool-collector-foundation-2026-08-31.md @@ -0,0 +1,60 @@ +# Tool collector foundation qualification — 2026-08-31 + +## Scope + +This receipt qualifies the unreleased `codevetter.tool-collection/v1` contract, +CLI wiring, and first Gitleaks adapter. It does not claim a sidecar is bundled or +that cargo-audit or Rust coverage executes in the product. + +## Contract + +- Input is one exact clean checked-out Git `base..head` range. +- Collector selection is explicit and duplicate selections are normalized. +- Product resolution accepts only an application-bundle sibling or an explicit + debug/test qualification override; release builds exclude that override and + arbitrary `PATH` discovery. +- Process launch uses no shell, a fixed minimal environment, null stdin, + kill-on-drop, a 120-second timeout, a 256 KiB diagnostic limit, and an 8 MiB + report limit. The JSON report is consumed from a private process pipe rather + than written to a temporary file. +- Tool evidence records exact version, resolution source, and binary SHA-256. +- Gitleaks runs with 100% redaction. Only normalized rule, description, + repository-relative file/line, commit, and fingerprint fields survive; + upstream `Secret` and `Match` fields are not represented by the Rust type. +- Every normalized finding must map to a commit and changed path in the resolved + source receipt or the collector returns an error. +- A finding exits 1, unavailable/error exits 2, and only fully clean collection + exits 0. These are CLI collection outcomes, not the overall CodeVetter + verification verdict. + +## Automated evidence + +Focused Rust tests prove that raw fixture secret and match values cannot enter +serialized receipts, missing product tools stay explicitly unavailable, and +CLI parsing requires a range plus supported explicit collectors. + +The local 8.30.1 Gitleaks binary was then exercised through the compiled CLI on +a disposable clean Git repository: + +| Trial | Observed result | +|---|---| +| Safe one-commit range | `clean`, zero findings, exit 0, 355 ms collector duration | +| Controlled custom-rule finding | `findings`, one normalized finding, exit 1, 383 ms collector duration | +| Missing cargo-audit and cargo-llvm-cov | both `unavailable`, exit 2 | + +The Gitleaks receipt recorded SHA-256 +`f414bc2fb952be6c9072b75cb411e3368614ef4b16d48dbd9ad238034afd2302`. +The controlled finding receipt contained the rule, relative path, line, commit, +and fingerprint, but not the matched fixture value or surrounding match. + +## Remaining gates + +- Pin, qualify, declare, sign, and smoke-test sidecar artifacts inside final app + bundles before claiming shipment. +- Package an offline RustSec advisory database before cargo-audit execution. +- Require the Rust LLVM tools component and implement LCOV changed-executable- + line accounting before cargo-llvm-cov execution. +- Compose collector evidence into CodeVetter's existing overall verdict policy; + do not substitute scanner exit codes for that policy. + +Issue #198 owns these gates. From b34ce70edbcfab8cd9463be87b7453de7e623ed3 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Mon, 31 Aug 2026 13:09:52 +0530 Subject: [PATCH 13/37] feat: integrate external evidence producers and MCP tooling --- PROJECT_STATUS.md | 26 + apps/desktop/.size-limit.json | 8 + apps/desktop/package.json | 11 +- apps/desktop/playwright.config.ts | 7 +- apps/desktop/src/pages/TRex.tsx | 7 +- .../tests/e2e/review-warm-evidence.spec.ts | 3 +- apps/desktop/tests/e2e/settings.spec.ts | 70 +- apps/desktop/tests/e2e/work.spec.ts | 1119 ----------------- docs/development/performance.md | 28 +- docs/development/testing.md | 31 +- docs/development/verification-receipts.md | 40 +- docs/knowledge/tooling-decisions.md | 22 + package.json | 5 + pnpm-lock.yaml | 107 ++ scripts/verification-receipts/analyze.mjs | 1 + scripts/verification-receipts/contracts.mjs | 85 +- scripts/verification-receipts/dogfood-cli.mjs | 102 ++ .../producer-adapters.mjs | 641 ++++++++++ .../verification-receipts.test.mjs | 129 ++ 19 files changed, 1224 insertions(+), 1218 deletions(-) create mode 100644 apps/desktop/.size-limit.json delete mode 100644 apps/desktop/tests/e2e/work.spec.ts create mode 100644 scripts/verification-receipts/dogfood-cli.mjs create mode 100644 scripts/verification-receipts/producer-adapters.mjs diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index fe784f9b..90809f5d 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -55,6 +55,32 @@ Internal (fleet): ## Timeline +- **2026-08-31 — External performance, testing, and MCP evidence adapters + (unreleased source):** verification-receipt ingestion now accepts Playwright + JSON, JUnit XML, LCOV, Cobertura XML, Lighthouse JSON, and Chrome trace JSON + while preserving raw artifact identity, hashing failure text, rejecting XML + entities, and keeping observation-only formats at `no_confidence`. A three-run + Lighthouse CI trial passed the proposed landing-page gates, but the dependency + was removed after the high-severity audit exposed an unpatched transitive + archive traversal. Upstream Size Limit now follows rather than replaces the + desktop's Tauri-aware bundle budgets. `pnpm verification:dogfood` now runs the + active Playwright and c8 suites, emits Playwright JSON, JUnit, LCOV, and + Cobertura through their built-in reporters, and successfully ingests all four + while retaining `no_confidence` outside their proof. The run also removed the + retired Work/Board E2E inventory and exposed then fixed a duplicate React key + in Testing. The maintainer Codex client has pinned + isolated Chrome DevTools and Playwright MCPs plus named CodeVetter runtime and + receipt MCPs. GitHub's checksum-verified official v1.11.0 local server replaces + the PAT-dependent remote registration, with read-only relevant toolsets and + narrow in-memory browser OAuth. Live repository, pull-request, Actions, issue, + and CodeQL reads verify the connection without exposing a write tool. The + packaged CodeVetter graph/history MCP is also enabled for the canonical + checkout from the app-generated opaque configuration. Its history and + tree-sitter structural indexes are current at the checked-out HEAD, and direct + protocol smoke tests returned bounded `history_search` and `graph_query` + results with local access-audit rows. No product release or deployment is + claimed; work is tracked in issue #200. + - **2026-08-31 — Bounded external collector foundation (unreleased source):** Added `codevetter collect` and a shared Rust `codevetter.tool-collection/v1` receipt for one exact clean checked-out Git range. The first adapter executes diff --git a/apps/desktop/.size-limit.json b/apps/desktop/.size-limit.json new file mode 100644 index 00000000..6452c5a9 --- /dev/null +++ b/apps/desktop/.size-limit.json @@ -0,0 +1,8 @@ +[ + { + "name": "Complete lazy JavaScript distribution (raw)", + "path": "out/assets/*.js", + "limit": "1800 kB", + "brotli": false + } +] diff --git a/apps/desktop/package.json b/apps/desktop/package.json index b04aa7de..3dbf837f 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -22,11 +22,11 @@ "qualify:agent-island:release": "node scripts/verify-agent-island-release.mjs", "tauri:dev": "pnpm prepare:mcp-sidecar && pnpm prepare:ccusage-sidecar && pnpm prepare:agent-island && tauri dev", "tauri:build": "tauri build", - "test": "npx playwright test", + "test": "playwright test", "test:unit": "node --import tsx --test \"src/**/*.test.ts\" && node --import tsx --test tests/qualification/warm-verification-live.test.ts", - "test:coverage": "c8 node --import tsx --test \"src/**/*.test.ts\" && node --import tsx --test tests/qualification/warm-verification-live.test.ts", - "test:e2e": "npx playwright test", - "test:e2e:ui": "npx playwright test --ui", + "test:coverage": "c8 --reporter=text --reporter=lcov --reporter=cobertura node --import tsx --test \"src/**/*.test.ts\" && node --import tsx --test tests/qualification/warm-verification-live.test.ts", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", "test:review-proof": "node --import tsx --test src/lib/review-proof.test.ts", "test:agent-fix-packet": "node --import tsx --test src/lib/agent-fix-packet.test.ts", "test:synthetic-qa": "node --import tsx --test src/lib/synthetic-qa/apply-evidence.test.ts src/lib/synthetic-qa/fixture-runner.test.ts", @@ -38,7 +38,8 @@ "verifyd": "node --import tsx src/lib/warm-verification/daemon-entry.ts", "test:verify": "node --import tsx --test \"src/lib/warm-verification/*.test.ts\" && node --import tsx --test tests/qualification/warm-verification-live.test.ts", "lint": "biome check .", - "bench:bundle": "node scripts/bundle-budget.mjs", + "bench:bundle": "node scripts/bundle-budget.mjs && size-limit", + "bench:bundle:upstream": "size-limit", "bench:history-ui": "node --import tsx scripts/history-workbench-benchmark.ts", "bench:mcp": "node scripts/mcp-benchmark.mjs", "bench:mcp:smoke": "node scripts/mcp-benchmark.mjs --smoke", diff --git a/apps/desktop/playwright.config.ts b/apps/desktop/playwright.config.ts index 859fc455..a291e93c 100644 --- a/apps/desktop/playwright.config.ts +++ b/apps/desktop/playwright.config.ts @@ -7,7 +7,12 @@ export default defineConfig({ timeout: 30_000, retries: 0, workers: 1, - reporter: [['list'], ['html', { open: 'never' }]], + reporter: [ + ['list'], + ['html', { open: 'never' }], + ['json', { outputFile: 'test-results/playwright.json' }], + ['junit', { outputFile: 'test-results/junit.xml', includeProjectInTestName: true }], + ], use: { baseURL: 'http://localhost:1420', viewport: { width: 1280, height: 800 }, diff --git a/apps/desktop/src/pages/TRex.tsx b/apps/desktop/src/pages/TRex.tsx index 11991dc9..8aa2e6ff 100644 --- a/apps/desktop/src/pages/TRex.tsx +++ b/apps/desktop/src/pages/TRex.tsx @@ -408,7 +408,7 @@ export default function TRex() { { @@ -439,7 +439,10 @@ export default function TRex() { onCleanup={handleWarmCleanup} /> - + diff --git a/apps/desktop/tests/e2e/review-warm-evidence.spec.ts b/apps/desktop/tests/e2e/review-warm-evidence.spec.ts index 3bb2e647..0cc4a3c0 100644 --- a/apps/desktop/tests/e2e/review-warm-evidence.spec.ts +++ b/apps/desktop/tests/e2e/review-warm-evidence.spec.ts @@ -412,7 +412,8 @@ test('Review presents deterministic coverage and rejected candidate counts', asy await expect(decision.getByRole('link', { name: 'Runtime evidence' })).toBeVisible(); }); -test('Review shows readiness for an external review agent', async ({ page }) => { +test('Review shows readiness for an external review agent', async ({ page, context }) => { + await context.grantPermissions(['clipboard-read', 'clipboard-write']); await installReviewMock(page, false); await navigateTo(page, '/review'); await waitForNoSpinners(page); diff --git a/apps/desktop/tests/e2e/settings.spec.ts b/apps/desktop/tests/e2e/settings.spec.ts index a5d350f5..a9f5648a 100644 --- a/apps/desktop/tests/e2e/settings.spec.ts +++ b/apps/desktop/tests/e2e/settings.spec.ts @@ -15,67 +15,10 @@ test.describe('Settings page', () => { consoleErrors.assertNoErrors(); }); - // ─── General tab ────────────────────────────────────────────────────── - - test('General tab is selected by default and shows AI Provider section', async ({ page }) => { - // "General" should be the active category - await expect(page.locator('text=General').first()).toBeVisible(); - - // AI Provider section heading - await expect(page.getByRole('heading', { name: 'AI Provider' })).toBeVisible(); - }); - - // ─── Provider dropdown ──────────────────────────────────────────────── - - test('Can select AI provider from dropdown', async ({ page }) => { - // The provider dropdown is a