From 320a9d3f29656d950135f0cb657abce08294a7fb Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Mon, 15 Jun 2026 11:32:45 -0300 Subject: [PATCH] chore(repo): nest quality-gate state under config/quality, declutter root (#3896) Move the committed quality-gate state files out of the repo root into config/quality/ and the v3.8.24 documentation audit into docs/ops/, then re-point every gate script, test and .gitignore entry at the new paths. Refresh docs/architecture/REPOSITORY_MAP.md (stale since v3.8.2) to match the current layout. Moved -> config/quality/: quality-baseline.json, complexity-baseline.json, duplication-baseline.json, file-size-baseline.json, test-discovery-baseline.json, dependency-allowlist.json, .license-allowlist.json (generated quality-metrics.json now written here too; still gitignored) Moved -> docs/ops/: DOCUMENTATION_AUDIT_REPORT.md (+ meta.json entry + fabricated-docs skip) Path updates: check-{complexity,duplication,file-size,test-discovery,deps, licenses,dead-code,cognitive-complexity,type-coverage}.mjs, check-quality- ratchet.mjs, collect-metrics.mjs, check-tracked-artifacts.mjs (+ its test and check-deps test). Also gitignore /logs/ (was untracked-not-ignored). Tracked root files: 56 -> 48. Tool configs left in root on purpose: most are auto-discovered there, and the tsconfig variants have location-relative files:[] arrays that would need 46 path rewrites for a 2-file gain. --- .gitignore | 7 +- .../quality/.license-allowlist.json | 0 .../quality/complexity-baseline.json | 0 .../quality/dependency-allowlist.json | 0 .../quality/duplication-baseline.json | 0 .../quality/file-size-baseline.json | 0 .../quality/quality-baseline.json | 0 .../quality/test-discovery-baseline.json | 0 docs/architecture/REPOSITORY_MAP.md | 287 ++++++++++-------- .../ops/DOCUMENTATION_AUDIT_REPORT.md | 0 docs/ops/meta.json | 3 +- scripts/check/check-cognitive-complexity.mjs | 2 +- scripts/check/check-complexity.mjs | 2 +- scripts/check/check-dead-code.mjs | 2 +- scripts/check/check-deps.mjs | 24 +- scripts/check/check-duplication.mjs | 14 +- scripts/check/check-fabricated-docs.mjs | 7 +- scripts/check/check-file-size.mjs | 19 +- scripts/check/check-licenses.mjs | 26 +- scripts/check/check-test-discovery.mjs | 2 +- scripts/check/check-tracked-artifacts.mjs | 9 +- scripts/check/check-type-coverage.mjs | 2 +- scripts/quality/check-quality-ratchet.mjs | 18 +- scripts/quality/collect-metrics.mjs | 5 +- .../build/check-tracked-artifacts.test.ts | 5 + tests/unit/check-deps.test.ts | 80 +++-- 26 files changed, 303 insertions(+), 211 deletions(-) rename .license-allowlist.json => config/quality/.license-allowlist.json (100%) rename complexity-baseline.json => config/quality/complexity-baseline.json (100%) rename dependency-allowlist.json => config/quality/dependency-allowlist.json (100%) rename duplication-baseline.json => config/quality/duplication-baseline.json (100%) rename file-size-baseline.json => config/quality/file-size-baseline.json (100%) rename quality-baseline.json => config/quality/quality-baseline.json (100%) rename test-discovery-baseline.json => config/quality/test-discovery-baseline.json (100%) rename DOCUMENTATION_AUDIT_REPORT.md => docs/ops/DOCUMENTATION_AUDIT_REPORT.md (100%) diff --git a/.gitignore b/.gitignore index cbe3af7f64..6597f7a315 100644 --- a/.gitignore +++ b/.gitignore @@ -203,8 +203,11 @@ pr_reviews*.json # internal setup prompts with personal credentials — never commit CODEX-SETUP-PROMPT.md -# Quality ratchet — métricas efêmeras (baseline é commitado, métricas não) -quality-metrics.json +# Quality ratchet — métricas efêmeras (baseline commitado em config/quality/; métricas não) +config/quality/quality-metrics.json + +# Runtime logs (diretório local, nunca versionado) +/logs/ -home-diegosouzapw-dev-automações-bots-yt-downloader-20260504 .txt -home-diegosouzapw-dev-automações-bots-yt-downloader-20260410 .txt docs/prompts/AGENT-OWNERSHIP-PROTOCOL.omniroute.md diff --git a/.license-allowlist.json b/config/quality/.license-allowlist.json similarity index 100% rename from .license-allowlist.json rename to config/quality/.license-allowlist.json diff --git a/complexity-baseline.json b/config/quality/complexity-baseline.json similarity index 100% rename from complexity-baseline.json rename to config/quality/complexity-baseline.json diff --git a/dependency-allowlist.json b/config/quality/dependency-allowlist.json similarity index 100% rename from dependency-allowlist.json rename to config/quality/dependency-allowlist.json diff --git a/duplication-baseline.json b/config/quality/duplication-baseline.json similarity index 100% rename from duplication-baseline.json rename to config/quality/duplication-baseline.json diff --git a/file-size-baseline.json b/config/quality/file-size-baseline.json similarity index 100% rename from file-size-baseline.json rename to config/quality/file-size-baseline.json diff --git a/quality-baseline.json b/config/quality/quality-baseline.json similarity index 100% rename from quality-baseline.json rename to config/quality/quality-baseline.json diff --git a/test-discovery-baseline.json b/config/quality/test-discovery-baseline.json similarity index 100% rename from test-discovery-baseline.json rename to config/quality/test-discovery-baseline.json diff --git a/docs/architecture/REPOSITORY_MAP.md b/docs/architecture/REPOSITORY_MAP.md index bddfefd9d5..c533392108 100644 --- a/docs/architecture/REPOSITORY_MAP.md +++ b/docs/architecture/REPOSITORY_MAP.md @@ -1,13 +1,13 @@ --- title: "Repository Map" -version: 3.8.2 -lastUpdated: 2026-05-13 +version: 3.8.26 +lastUpdated: 2026-06-15 --- # Repository Map > **One-line description for every directory and root file.** -> Last updated: 2026-05-13 — OmniRoute v3.8.0 +> Last updated: 2026-06-15 — OmniRoute v3.8.26 > > Use this map to navigate the codebase quickly. For deep dives, follow links to dedicated docs. @@ -23,8 +23,13 @@ OmniRoute/ ├── docs/ # Public documentation (you are here) ├── tests/ # All test suites (unit, integration, e2e, protocols-e2e) ├── public/ # Next.js static assets, PWA manifest, service worker, icons -├── config/ # Static config files +├── config/ # Static config + quality-gate state (i18n, payloadRules, quality/) ├── images/ # Marketing / README image assets +├── @omniroute/ # Publishable companion packages (opencode-plugin, opencode-provider) +├── skills/ # CLI/agent skill packs (cli-* + omni-* + config-codex-cli) +├── examples/ # Sample plugins + omniroute-cmd-hello starter +├── contrib/ # Community contributions (podman/) +├── .source/ # Fumadocs source config (source.config.mjs + server/browser/dynamic) ├── .github/ # GitHub Actions workflows + issue templates + PR template ├── .husky/ # Git hooks (pre-commit, pre-push) ├── .claude/ # Claude Code slash commands (project-scoped) @@ -34,6 +39,7 @@ OmniRoute/ ├── _mono_repo/ # Historic subprojects (cloud, site, vscode-extension) ├── _references/ # Read-only reference clones from related OSS projects ├── _tasks/ # Per-release task tracking files (informal) +├── .build/ .worktrees/ dist/ # local build / git-worktree / build-output scratch (gitignored) ├── .issues/ # Local issue cache (gitignored) ├── .playwright-mcp/ # Playwright MCP test artifacts ├── coverage/ # c8 coverage output (gitignored) @@ -48,45 +54,63 @@ OmniRoute/ ## Root files -| File | Purpose | -| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| **README.md** | Marketing landing page + quick start + feature matrix (see also `llm.txt`) | -| **CHANGELOG.md** | Per-release changelog (auto-generated by `/version-bump-cc` skill) | -| **LICENSE** | MIT license text | -| **CLAUDE.md** | Project rules for Claude Code agents (hard rules, conventions, scenarios) | -| **AGENTS.md** | Same as CLAUDE.md but for non-Claude AI agents (Codex, Cursor, etc.) | -| **GEMINI.md** | Concise rules for Gemini-based agents (subset of CLAUDE.md) | -| **CONTRIBUTING.md** | Contributor guide: setup, conventional commits, testing, PR flow | -| **SECURITY.md** | Vulnerability reporting policy, supported versions, threat model | -| **CODE_OF_CONDUCT.md** | Contributor Covenant — community behavior expectations | -| **llm.txt** | Plain-text landing optimized for LLM crawlers (SEO for AI assistants) | -| **Tuto_Qdrant.md** | Tutorial for enabling Qdrant vector memory — **integration currently dormant** (see banner; primary memory docs in `docs/frameworks/MEMORY.md`) | -| **package.json** | npm manifest, scripts, dependencies, engines, c8 coverage gate | -| **package-lock.json** | Locked dependency tree | -| **tsconfig.json** | Root TypeScript config | -| **tsconfig.typecheck-core.json** | Typecheck config for `src/` core | -| **tsconfig.typecheck-noimplicit-core.json** | Strict (`noImplicitAny`) typecheck | -| **tsconfig.tsbuildinfo** | TS incremental build cache (gitignored) | -| **next.config.mjs** | Next.js 16 build configuration (standalone output) | -| **next-env.d.ts** | Next.js auto-generated env types | -| **eslint.config.mjs** | ESLint flat config (rules per project area) | -| **prettier.config.mjs** | Prettier formatting rules | -| **postcss.config.mjs** | PostCSS config for Tailwind/CSS pipeline | -| **playwright.config.ts** | Playwright E2E test config | -| **vitest.config.ts** | Vitest config (default suite) | -| **vitest.mcp.config.ts** | Vitest config for MCP server / autoCombo / cache suites | -| **sonar-project.properties** | SonarQube/SonarCloud config (code quality) | -| **Dockerfile** | Multi-stage Docker build (builder → runner-base → runner-cli) | -| **docker-compose.yml** | Dev compose with 4 profiles (base, cli, host, cliproxyapi) + redis sidecar | -| **docker-compose.prod.yml** | Production compose (port 20130, redis, named volumes) | -| **.dockerignore** | Files excluded from Docker context | -| **fly.toml** | Fly.io deployment config (region `sin`, port 20128, /data volume) | -| **.env.example** | Template env file (815 lines, auto-copied to `.env` on first install) | -| **.gitignore** | Git ignore patterns | -| **.npmignore** | npm publish exclusion list | -| **.npmrc** | npm config (registry, lockfile policy) | -| **.node-version** | Node version pin (used by nvm-compatible tools) | -| **.nvmrc** | Node version pin for nvm | +| File | Purpose | +| ------------------------------------------- | ---------------------------------------------------------------------------------------- | +| **README.md** | Marketing landing page + quick start + feature matrix (see also `llm.txt`) | +| **CHANGELOG.md** | Per-release changelog (auto-generated by `/version-bump-cc` skill) | +| **LICENSE** | MIT license text | +| **CLAUDE.md** | Project rules for Claude Code agents (hard rules, conventions, scenarios) | +| **AGENTS.md** | Same as CLAUDE.md but for non-Claude AI agents (Codex, Cursor, etc.) | +| **GEMINI.md** | Concise rules for Gemini-based agents (subset of CLAUDE.md) | +| **CONTRIBUTING.md** | Contributor guide: setup, conventional commits, testing, PR flow | +| **SECURITY.md** | Vulnerability reporting policy, supported versions, threat model | +| **CODE_OF_CONDUCT.md** | Contributor Covenant — community behavior expectations | +| **llm.txt** | Plain-text landing optimized for LLM crawlers (SEO for AI assistants) | +| **package.json** | npm manifest, scripts, dependencies, engines, c8 coverage gate | +| **package-lock.json** | Locked dependency tree | +| **tsconfig.json** | Root TypeScript config | +| **tsconfig.typecheck-core.json** | Typecheck config for `src/` core | +| **tsconfig.typecheck-noimplicit-core.json** | Strict (`noImplicitAny`) typecheck | +| **tsconfig.tsbuildinfo** | TS incremental build cache (gitignored) | +| **next.config.mjs** | Next.js 16 build configuration (standalone output) | +| **next-env.d.ts** | Next.js auto-generated env types | +| **eslint.config.mjs** | ESLint flat config (rules per project area) | +| **prettier.config.mjs** | Prettier formatting rules | +| **postcss.config.mjs** | PostCSS config for Tailwind/CSS pipeline | +| **playwright.config.ts** | Playwright E2E test config | +| **vitest.config.ts** | Vitest config (default suite) | +| **vitest.mcp.config.ts** | Vitest config for MCP server / autoCombo / cache suites | +| **sonar-project.properties** | SonarQube/SonarCloud config (code quality) | +| **Dockerfile** | Multi-stage Docker build (builder → runner-base → runner-cli) | +| **docker-compose.yml** | Dev compose with 4 profiles (base, cli, host, cliproxyapi) + redis sidecar | +| **docker-compose.prod.yml** | Production compose (port 20130, redis, named volumes) | +| **.dockerignore** | Files excluded from Docker context | +| **fly.toml** | Fly.io deployment config (region `sin`, port 20128, /data volume) | +| **.env.example** | Template env file (auto-copied to `.env` on first install) | +| **.gitignore** | Git ignore patterns | +| **.npmignore** | npm publish exclusion list | +| **.npmrc** | npm config (registry, lockfile policy) | +| **.node-version** | Node version pin (used by nvm-compatible tools) | +| **.nvmrc** | Node version pin for nvm | +| **eslint.complexity.config.mjs** | ESLint config for the complexity ratchet (`scripts/check/check-complexity.mjs --config`) | +| **eslint.sonarjs.config.mjs** | ESLint config for SonarJS rules (cognitive complexity / duplication) | +| **source.config.ts** | Fumadocs `defineDocs` source config (feeds `.source/`) | +| **knip.json** | Knip config — unused files/exports/deps (feeds the dead-code gate) | +| **stryker.conf.json** | Stryker mutation-testing config | +| **.size-limit.json** | size-limit bundle budget config | +| **semcheck.yaml** | semcheck (spec↔code drift) config | +| **promptfooconfig.yaml** | promptfoo eval config | +| **.gitleaks.toml** | gitleaks secret-scan ruleset | +| **.zizmor.yml** | zizmor GitHub-Actions security-lint config | +| **socket.yml** | Socket.dev supply-chain config | +| **news.json** | In-app release-notes feed (read by `src/shared/utils/releaseNotes.ts`) | +| **flake.nix** / **flake.lock** | Nix dev-shell definition + lock | +| **.env** | Local secrets (gitignored — generated from `.env.example`) | + +> **Moved out of the root in v3.8.26 (declutter):** +> +> - **→ `config/quality/`:** `quality-baseline.json`, `complexity-baseline.json`, `duplication-baseline.json`, `file-size-baseline.json`, `test-discovery-baseline.json`, `dependency-allowlist.json`, `.license-allowlist.json`, and the generated `quality-metrics.json` (gitignored). See [`## config/`](#config--static-configs--quality-gate-state). +> - **→ `docs/ops/`:** `DOCUMENTATION_AUDIT_REPORT.md`. --- @@ -117,84 +141,84 @@ src/ ### `src/app/` — App Router (Next.js 16) -| Path | Purpose | -| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `app/api/v1/` | Public OpenAI-compat API (~25 sub-routes: chat, completions, embeddings, files, batches, audio, images, videos, music, rerank, moderations, search, ws, agents, accounts, providers, etc.) | -| `app/api/v1beta/` | Gemini-style API endpoints | -| `app/api/playground/` | Playground Studio routes: `improve-prompt/` (POST — LLM prompt rewriter), `presets/` (GET list / POST create), `presets/[id]/` (GET / PUT / DELETE) — see `docs/frameworks/PLAYGROUND_STUDIO.md` | -| `app/api/` (non-v1) | Management/admin routes (~60 directories: providers, combos, settings, mcp, a2a, evals, memory, skills, webhooks, compliance, resilience, monitoring, tunnels, cli-tools, etc.) | -| `app/api/tools/agent-bridge/` | AgentBridge REST API — 12 routes (server control, agent state/DNS/mappings, bypass, cert, upstream-CA). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/AGENTBRIDGE.md §7`. | -| `app/api/tools/traffic-inspector/` | Traffic Inspector REST + WS API — 16+ routes (requests, sessions, hosts, capture-modes, export, ws). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/TRAFFIC_INSPECTOR.md §8`. | -| `app/a2a/` | A2A JSON-RPC 2.0 entry point (`POST /a2a`) | -| `app/.well-known/agent.json/` | A2A Agent Card (discovery) | -| `app/(dashboard)/dashboard/` | Dashboard UI pages (~35 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, activity, etc.) | -| `app/(dashboard)/dashboard/search-tools/` | Search Tools Studio UI (3 tabs: Search/Scrape/Compare + SearchConceptCard + ProviderCatalog) — see `docs/frameworks/SEARCH_TOOLS_STUDIO.md` | -| `app/(dashboard)/dashboard/` | Dashboard UI pages (~30 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, etc.) | +| Path | Purpose | +| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `app/api/v1/` | Public OpenAI-compat API (~25 sub-routes: chat, completions, embeddings, files, batches, audio, images, videos, music, rerank, moderations, search, ws, agents, accounts, providers, etc.) | +| `app/api/v1beta/` | Gemini-style API endpoints | +| `app/api/playground/` | Playground Studio routes: `improve-prompt/` (POST — LLM prompt rewriter), `presets/` (GET list / POST create), `presets/[id]/` (GET / PUT / DELETE) — see `docs/frameworks/PLAYGROUND_STUDIO.md` | +| `app/api/` (non-v1) | Management/admin routes (~60 directories: providers, combos, settings, mcp, a2a, evals, memory, skills, webhooks, compliance, resilience, monitoring, tunnels, cli-tools, etc.) | +| `app/api/tools/agent-bridge/` | AgentBridge REST API — 12 routes (server control, agent state/DNS/mappings, bypass, cert, upstream-CA). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/AGENTBRIDGE.md §7`. | +| `app/api/tools/traffic-inspector/` | Traffic Inspector REST + WS API — 16+ routes (requests, sessions, hosts, capture-modes, export, ws). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/TRAFFIC_INSPECTOR.md §8`. | +| `app/a2a/` | A2A JSON-RPC 2.0 entry point (`POST /a2a`) | +| `app/.well-known/agent.json/` | A2A Agent Card (discovery) | +| `app/(dashboard)/dashboard/` | Dashboard UI pages (~35 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, activity, etc.) | +| `app/(dashboard)/dashboard/search-tools/` | Search Tools Studio UI (3 tabs: Search/Scrape/Compare + SearchConceptCard + ProviderCatalog) — see `docs/frameworks/SEARCH_TOOLS_STUDIO.md` | +| `app/(dashboard)/dashboard/` | Dashboard UI pages (~30 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, etc.) | | `app/(dashboard)/dashboard/memory/` | Memory Studio (plan 21): `page.tsx` (3-tab shell), `components/` (MemoryConceptCard, MemoryEngineStatus, EmbeddingSourceSelector, EditMemoryModal, RetrievePreview, QdrantConfigCard, RerankConfigCard), `components/tabs/` (MemoriesTab, PlaygroundTab, EngineTab), `hooks/` (useEngineStatus, useMemorySettings) | -| `app/(dashboard)/dashboard/tools/agent-bridge/` | AgentBridge dashboard page — server card, 9 agent cards, setup wizard, model mapping, bypass list. i18n PT-BR + EN. See `docs/frameworks/AGENTBRIDGE.md`. | -| `app/(dashboard)/dashboard/tools/traffic-inspector/` | Traffic Inspector dashboard page — DevTools split, 7 detail tabs, 4 capture mode toggles, session recorder, context colorization. i18n PT-BR + EN. See `docs/frameworks/TRAFFIC_INSPECTOR.md`. | -| `app/(dashboard)/dashboard/activity/` | Activity feed page (Group B): `page.tsx` (server) + `ActivityFeedClient.tsx` + `components/{ActivityFeed,ActivityItem,DayHeader,EventTypeFilter}.tsx` — see `docs/architecture/MONITORING_SECTIONS.md` | -| `app/(dashboard)/dashboard/costs/quota-share/` | Quota Sharing page (Group B): `QuotaSharePageClient.tsx` + `components/{PoolCard,DimensionBar,AllocationTable,BurnRateChart,QuotaConceptCard,CreatePoolModal,EditAllocationsModal}.tsx` + `hooks/{usePools,usePoolUsage,useLocalStoragePoolMigration}.ts` | -| `app/(dashboard)/dashboard/costs/quota-share/plans/` | Provider plan config page (Group B): `page.tsx` + `ProviderPlanConfigClient.tsx` — quota dimensions per connection override | -| `app/docs/` | Embedded documentation viewer (renders `docs/*.md`) | -| `app/landing/` | Marketing landing page | -| `app/login/`, `forgot-password/`, `forbidden/` | Auth-related pages | -| `app/{400,401,403,408,429,500,502,503}/` | HTTP error pages | -| `app/maintenance/`, `offline/`, `status/`, `privacy/`, `terms/`, `callback/` | Static/status pages | -| `app/layout.tsx`, `page.tsx`, `manifest.ts`, `globals.css` | Root layout, home, PWA manifest, global CSS | -| `app/error.tsx`, `global-error.tsx`, `not-found.tsx`, `loading.tsx` | Error boundaries | +| `app/(dashboard)/dashboard/tools/agent-bridge/` | AgentBridge dashboard page — server card, 9 agent cards, setup wizard, model mapping, bypass list. i18n PT-BR + EN. See `docs/frameworks/AGENTBRIDGE.md`. | +| `app/(dashboard)/dashboard/tools/traffic-inspector/` | Traffic Inspector dashboard page — DevTools split, 7 detail tabs, 4 capture mode toggles, session recorder, context colorization. i18n PT-BR + EN. See `docs/frameworks/TRAFFIC_INSPECTOR.md`. | +| `app/(dashboard)/dashboard/activity/` | Activity feed page (Group B): `page.tsx` (server) + `ActivityFeedClient.tsx` + `components/{ActivityFeed,ActivityItem,DayHeader,EventTypeFilter}.tsx` — see `docs/architecture/MONITORING_SECTIONS.md` | +| `app/(dashboard)/dashboard/costs/quota-share/` | Quota Sharing page (Group B): `QuotaSharePageClient.tsx` + `components/{PoolCard,DimensionBar,AllocationTable,BurnRateChart,QuotaConceptCard,CreatePoolModal,EditAllocationsModal}.tsx` + `hooks/{usePools,usePoolUsage,useLocalStoragePoolMigration}.ts` | +| `app/(dashboard)/dashboard/costs/quota-share/plans/` | Provider plan config page (Group B): `page.tsx` + `ProviderPlanConfigClient.tsx` — quota dimensions per connection override | +| `app/docs/` | Embedded documentation viewer (renders `docs/*.md`) | +| `app/landing/` | Marketing landing page | +| `app/login/`, `forgot-password/`, `forbidden/` | Auth-related pages | +| `app/{400,401,403,408,429,500,502,503}/` | HTTP error pages | +| `app/maintenance/`, `offline/`, `status/`, `privacy/`, `terms/`, `callback/` | Static/status pages | +| `app/layout.tsx`, `page.tsx`, `manifest.ts`, `globals.css` | Root layout, home, PWA manifest, global CSS | +| `app/error.tsx`, `global-error.tsx`, `not-found.tsx`, `loading.tsx` | Error boundaries | ### `src/lib/` — Core libraries (~50 modules) -| Module | Purpose | -| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `a2a/` | A2A protocol task manager, skills (5), streaming | -| `acp/` | CLI Agent Registry (local CLI discovery — see `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`) | -| `api/` | Shared API helpers (`requireManagementAuth`, validation) | -| `auth/` | Session, password hashing, token validation | -| `batches/` | OpenAI Batches API handlers | -| `catalog/` | Provider catalog Zod validation + capability resolution | -| `cloudAgent/` | Cloud Agents (Codex Cloud, Devin, Jules) — see `docs/frameworks/CLOUD_AGENT.md` | -| `combos/` | Combo resolution + reorder helpers | -| `audit/` | Activity feed helpers: `highLevelActions.ts` (allowlist + `isHighLevelAction()`), `activityIcons.ts` (action → icon/verb map), `timeline.ts` (groupByDay/relativeTime) — see `docs/architecture/MONITORING_SECTIONS.md` | -| `compliance/` | Audit log + provider audit — see `docs/security/COMPLIANCE.md` | -| `compression/` | Compression engine glue (engines live in `open-sse/services/compression/`) | -| `config/` | Runtime config helpers | -| `db/` | 45+ domain DB modules + 55 migrations (always go through here for SQLite) | +| Module | Purpose | +| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `a2a/` | A2A protocol task manager, skills (5), streaming | +| `acp/` | CLI Agent Registry (local CLI discovery — see `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`) | +| `api/` | Shared API helpers (`requireManagementAuth`, validation) | +| `auth/` | Session, password hashing, token validation | +| `batches/` | OpenAI Batches API handlers | +| `catalog/` | Provider catalog Zod validation + capability resolution | +| `cloudAgent/` | Cloud Agents (Codex Cloud, Devin, Jules) — see `docs/frameworks/CLOUD_AGENT.md` | +| `combos/` | Combo resolution + reorder helpers | +| `audit/` | Activity feed helpers: `highLevelActions.ts` (allowlist + `isHighLevelAction()`), `activityIcons.ts` (action → icon/verb map), `timeline.ts` (groupByDay/relativeTime) — see `docs/architecture/MONITORING_SECTIONS.md` | +| `compliance/` | Audit log + provider audit — see `docs/security/COMPLIANCE.md` | +| `compression/` | Compression engine glue (engines live in `open-sse/services/compression/`) | +| `config/` | Runtime config helpers | +| `db/` | 45+ domain DB modules + 55 migrations (always go through here for SQLite) | | `quota/` | Quota Sharing Engine: `dimensions.ts` (types/Zod), `types.ts` (QuotaStore interface), `sqliteQuotaStore.ts`, `redisQuotaStore.ts`, `storeFactory.ts`, `fairShare.ts`, `burnRate.ts`, `planResolver.ts`, `planRegistry.ts`, `saturationSignals.ts`, `enforce.ts`, `spendRecorder.ts` — see `docs/routing/QUOTA_SHARE.md` | -| `display/` | UI formatting helpers (cost, latency, etc.) | -| `embeddings/` | Embeddings service helpers | -| `env/` | Env variable parsing + validation | -| `evals/` | Eval framework (suites, runner, runtime) — see `docs/frameworks/EVALS.md` | -| `guardrails/` | PII masker, prompt injection, vision bridge — see `docs/security/GUARDRAILS.md` | -| `jobs/` | Background jobs (cron-like) | -| `memory/` | Conversational memory (SQLite FTS5 + sqlite-vec hybrid RRF + Qdrant tier 2) — see `docs/frameworks/MEMORY.md` | -| `memory/embedding/` | Multi-source embedding layer: `index.ts` (resolver), `remote.ts`, `staticPotion.ts`, `transformersLocal.ts`, `cache.ts`, `types.ts` (plan 21) | -| `memory/vectorStore.ts` | sqlite-vec v0.1.9 wrapper — KNN brute-force + hybrid RRF (FTS5 + vector, k=60). Lazy-init, degrades gracefully when sqlite-vec unavailable. (plan 21) | -| `memory/reindex.ts` | `runReindexBatch()` — processes memories with `needs_reindex=1` in background; called by `POST /api/memory/reindex` and lazy-backfill path. (plan 21) | -| `monitoring/` | Health checks, metrics emission | -| `oauth/` | OAuth flows for 14 providers (claude, codex, antigravity, cursor, github, gemini, kimi-coding, kilocode, cline, qwen, kiro, qoder, gitlab-duo, windsurf) | -| `plugins/` | Plugin registry | -| `promptCache/` | Anthropic-style prompt cache breakpoints | -| `skills/` | Skills framework (built-in + marketplace + SkillsSH) — see `docs/frameworks/SKILLS.md` | -| `playground/` | Playground Studio shared helpers: `codeExport.ts` (curl/Python/TS generator), `promptImprover.ts` (meta-prompt builder), `streamMetrics.ts` (pure TTFT/TPS), `types.ts` (pricing table) — see `docs/frameworks/PLAYGROUND_STUDIO.md` | -| `webhookDispatcher.ts` | HMAC webhook delivery — see `docs/frameworks/WEBHOOKS.md` | -| `cloudflaredTunnel.ts`, `ngrokTunnel.ts` | Tunnel managers — see `docs/ops/TUNNELS_GUIDE.md` | -| `oneproxySync.ts`, `oneproxyRotator.ts` | 1proxy free proxy marketplace — see `docs/ops/PROXY_GUIDE.md` | -| `cloudSync.ts`, `initCloudSync.ts` | Optional cloud sync of state | -| `localDb.ts` | Re-export barrel for db modules (no logic — re-exports only) | -| `cacheLayer.ts`, `idempotencyLayer.ts` | Request caching + idempotency | -| (~30 more top-level files) | Specialized helpers (logEnv, modelsDevSync, piiSanitizer, etc.) | +| `display/` | UI formatting helpers (cost, latency, etc.) | +| `embeddings/` | Embeddings service helpers | +| `env/` | Env variable parsing + validation | +| `evals/` | Eval framework (suites, runner, runtime) — see `docs/frameworks/EVALS.md` | +| `guardrails/` | PII masker, prompt injection, vision bridge — see `docs/security/GUARDRAILS.md` | +| `jobs/` | Background jobs (cron-like) | +| `memory/` | Conversational memory (SQLite FTS5 + sqlite-vec hybrid RRF + Qdrant tier 2) — see `docs/frameworks/MEMORY.md` | +| `memory/embedding/` | Multi-source embedding layer: `index.ts` (resolver), `remote.ts`, `staticPotion.ts`, `transformersLocal.ts`, `cache.ts`, `types.ts` (plan 21) | +| `memory/vectorStore.ts` | sqlite-vec v0.1.9 wrapper — KNN brute-force + hybrid RRF (FTS5 + vector, k=60). Lazy-init, degrades gracefully when sqlite-vec unavailable. (plan 21) | +| `memory/reindex.ts` | `runReindexBatch()` — processes memories with `needs_reindex=1` in background; called by `POST /api/memory/reindex` and lazy-backfill path. (plan 21) | +| `monitoring/` | Health checks, metrics emission | +| `oauth/` | OAuth flows for 14 providers (claude, codex, antigravity, cursor, github, gemini, kimi-coding, kilocode, cline, qwen, kiro, qoder, gitlab-duo, windsurf) | +| `plugins/` | Plugin registry | +| `promptCache/` | Anthropic-style prompt cache breakpoints | +| `skills/` | Skills framework (built-in + marketplace + SkillsSH) — see `docs/frameworks/SKILLS.md` | +| `playground/` | Playground Studio shared helpers: `codeExport.ts` (curl/Python/TS generator), `promptImprover.ts` (meta-prompt builder), `streamMetrics.ts` (pure TTFT/TPS), `types.ts` (pricing table) — see `docs/frameworks/PLAYGROUND_STUDIO.md` | +| `webhookDispatcher.ts` | HMAC webhook delivery — see `docs/frameworks/WEBHOOKS.md` | +| `cloudflaredTunnel.ts`, `ngrokTunnel.ts` | Tunnel managers — see `docs/ops/TUNNELS_GUIDE.md` | +| `oneproxySync.ts`, `oneproxyRotator.ts` | 1proxy free proxy marketplace — see `docs/ops/PROXY_GUIDE.md` | +| `cloudSync.ts`, `initCloudSync.ts` | Optional cloud sync of state | +| `localDb.ts` | Re-export barrel for db modules (no logic — re-exports only) | +| `cacheLayer.ts`, `idempotencyLayer.ts` | Request caching + idempotency | +| (~30 more top-level files) | Specialized helpers (logEnv, modelsDevSync, piiSanitizer, etc.) | ### `src/db/` — Database (45+ modules + 55 migrations) -| Subdir | Purpose | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `db/core.ts` | `getDbInstance()` singleton with WAL journaling | -| `db/migrations/` | Versioned SQL files (idempotent, transactional). `073_memory_vec.sql` adds `memory_vec_meta` + `needs_reindex` column (plan 21). | -| `db/playgroundPresets.ts` | CRUD module for Playground Studio presets (`listPlaygroundPresets`, `getPlaygroundPreset`, `createPlaygroundPreset`, `updatePlaygroundPreset`, `deletePlaygroundPreset`) | -| `db/memoryVec.ts`| CRUD for `memory_vec_meta` (active_dim, embedding_signature, last_reset_at, vec_loaded) + `markMemoryNeedsReindex`, `getMemoryReindexQueue`, etc. (plan 21) | -| `db/.ts` | One module per domain: providers, combos, apiKeys, users, sessions, usage, audit*log, webhooks, skills, memory_entries, cloud_agent_tasks, evals*\*, reasoning_cache, etc. | +| Subdir | Purpose | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `db/core.ts` | `getDbInstance()` singleton with WAL journaling | +| `db/migrations/` | Versioned SQL files (idempotent, transactional). `073_memory_vec.sql` adds `memory_vec_meta` + `needs_reindex` column (plan 21). | +| `db/playgroundPresets.ts` | CRUD module for Playground Studio presets (`listPlaygroundPresets`, `getPlaygroundPreset`, `createPlaygroundPreset`, `updatePlaygroundPreset`, `deletePlaygroundPreset`) | +| `db/memoryVec.ts` | CRUD for `memory_vec_meta` (active_dim, embedding_signature, last_reset_at, vec_loaded) + `markMemoryNeedsReindex`, `getMemoryReindexQueue`, etc. (plan 21) | +| `db/.ts` | One module per domain: providers, combos, apiKeys, users, sessions, usage, audit*log, webhooks, skills, memory_entries, cloud_agent_tasks, evals*\*, reasoning_cache, etc. | ### `src/domain/` @@ -455,9 +479,24 @@ open-sse/ --- -## `config/` — Static Configs +## `config/` — Static Configs + Quality-Gate State -Shipped configuration templates and sample files (referenced by setup wizard). +Shipped configuration templates plus the committed quality-gate baselines +(moved here from the repo root in v3.8.26 to keep the root lean). + +| Path | Purpose | +| --------------------------------------------- | -------------------------------------------------------------------------------- | +| `config/i18n.json` | Locale list + metadata (canonical source for the 42-locale count) | +| `config/i18n-schema.json` | JSON schema validating `i18n.json` | +| `config/payloadRules.json` | Upstream payload sanitization rules | +| `config/quality/quality-baseline.json` | Multi-metric ratchet baseline (`scripts/quality/check-quality-ratchet.mjs`) | +| `config/quality/complexity-baseline.json` | Frozen ESLint-complexity baseline (`check-complexity.mjs`) | +| `config/quality/duplication-baseline.json` | Frozen jscpd duplication baseline (`check-duplication.mjs`) | +| `config/quality/file-size-baseline.json` | Frozen per-file size baseline (`check-file-size.mjs`) | +| `config/quality/test-discovery-baseline.json` | Frozen orphan-test baseline (`check-test-discovery.mjs`) | +| `config/quality/dependency-allowlist.json` | Approved dependencies allowlist (`check-deps.mjs`) | +| `config/quality/.license-allowlist.json` | SPDX license allowlist (`check-licenses.mjs`) | +| `config/quality/quality-metrics.json` | Ephemeral collected metrics (generated by `collect-metrics.mjs`; **gitignored**) | --- @@ -484,15 +523,15 @@ Shipped configuration templates and sample files (referenced by setup wizard). ## `.claude/` — Claude Code Slash Commands -| File | Purpose | -| ----------------------------------------------------------------- | -------------------------------------------------- | -| `commands/version-bump-cc.md` | `/version-bump-cc` — bump version + auto-changelog | -| `commands/generate-release-cc.md` | `/generate-release-cc` — full release workflow | -| `commands/deploy-vps-{local,akamai,both}-cc.md` | Deploy to VPS | -| `commands/capture-release-evidences-cc.md` | Browser-record new features as WebP | -| `commands/review-{prs,discussions}-cc.md` | Triage GitHub PRs/discussions | -| `commands/{review-issues,implement-features}-cc.md` | Issue workflows | -| `settings.local.json` | Per-project Claude Code settings | +| File | Purpose | +| --------------------------------------------------- | -------------------------------------------------- | +| `commands/version-bump-cc.md` | `/version-bump-cc` — bump version + auto-changelog | +| `commands/generate-release-cc.md` | `/generate-release-cc` — full release workflow | +| `commands/deploy-vps-{local,akamai,both}-cc.md` | Deploy to VPS | +| `commands/capture-release-evidences-cc.md` | Browser-record new features as WebP | +| `commands/review-{prs,discussions}-cc.md` | Triage GitHub PRs/discussions | +| `commands/{review-issues,implement-features}-cc.md` | Issue workflows | +| `settings.local.json` | Per-project Claude Code settings | --- diff --git a/DOCUMENTATION_AUDIT_REPORT.md b/docs/ops/DOCUMENTATION_AUDIT_REPORT.md similarity index 100% rename from DOCUMENTATION_AUDIT_REPORT.md rename to docs/ops/DOCUMENTATION_AUDIT_REPORT.md diff --git a/docs/ops/meta.json b/docs/ops/meta.json index b2248086f1..dfc1af0180 100644 --- a/docs/ops/meta.json +++ b/docs/ops/meta.json @@ -8,6 +8,7 @@ "PROXY_GUIDE", "SQLITE_RUNTIME", "COVERAGE_PLAN", - "E2E_DASHBOARD_SHAKEDOWN_v3.8.0" + "E2E_DASHBOARD_SHAKEDOWN_v3.8.0", + "DOCUMENTATION_AUDIT_REPORT" ] } diff --git a/scripts/check/check-cognitive-complexity.mjs b/scripts/check/check-cognitive-complexity.mjs index 90ea5d3e55..663fd8eb44 100644 --- a/scripts/check/check-cognitive-complexity.mjs +++ b/scripts/check/check-cognitive-complexity.mjs @@ -31,7 +31,7 @@ const ESLINT_BIN = path.join(ROOT, "node_modules", ".bin", "eslint"); const BASELINE_PATH = path.resolve( process.argv.includes("--baseline") ? process.argv[process.argv.indexOf("--baseline") + 1] - : path.join(ROOT, "quality-baseline.json") + : path.join(ROOT, "config/quality/quality-baseline.json") ); const ESLINT_ARGS = [ diff --git a/scripts/check/check-complexity.mjs b/scripts/check/check-complexity.mjs index bc2f8c42b4..375512bdf8 100644 --- a/scripts/check/check-complexity.mjs +++ b/scripts/check/check-complexity.mjs @@ -19,7 +19,7 @@ const ROOT = process.cwd(); const BASELINE_PATH = path.resolve( process.argv.includes("--baseline") ? process.argv[process.argv.indexOf("--baseline") + 1] - : path.join(ROOT, "complexity-baseline.json") + : path.join(ROOT, "config/quality/complexity-baseline.json") ); const UPDATE = process.argv.includes("--update"); const CONFIG_PATH = path.join(ROOT, "eslint.complexity.config.mjs"); diff --git a/scripts/check/check-dead-code.mjs b/scripts/check/check-dead-code.mjs index 3e93890a07..1c7f5c5e34 100644 --- a/scripts/check/check-dead-code.mjs +++ b/scripts/check/check-dead-code.mjs @@ -28,7 +28,7 @@ const UPDATE = process.argv.includes("--update"); const BASELINE_PATH = path.resolve( process.argv.includes("--baseline") ? process.argv[process.argv.indexOf("--baseline") + 1] - : path.join(ROOT, "quality-baseline.json") + : path.join(ROOT, "config/quality/quality-baseline.json") ); /** diff --git a/scripts/check/check-deps.mjs b/scripts/check/check-deps.mjs index 1cc88f29c8..cb3eb28096 100644 --- a/scripts/check/check-deps.mjs +++ b/scripts/check/check-deps.mjs @@ -29,7 +29,7 @@ import { execFileSync } from "node:child_process"; import { assertNoStale } from "./lib/allowlist.mjs"; const ROOT = process.cwd(); -const ALLOWLIST_PATH = path.join(ROOT, "dependency-allowlist.json"); +const ALLOWLIST_PATH = path.join(ROOT, "config/quality/dependency-allowlist.json"); // Directories to exclude when discovering package.json files. // Using a set of path segment prefixes (relative to ROOT, forward slashes). @@ -141,16 +141,12 @@ export function queryNpmRegistry(pkgName, timeoutMs = 8000) { // Scope packages need URL-encoding for the `npm view` command. // `npm view` accepts scoped packages natively — no encoding needed. try { - const raw = execFileSync( - "npm", - ["view", pkgName, "time.created", "--json"], - { - encoding: "utf8", - timeout: timeoutMs, - // Suppress npm progress/warn output on stderr - stdio: ["ignore", "pipe", "pipe"], - } - ); + const raw = execFileSync("npm", ["view", pkgName, "time.created", "--json"], { + encoding: "utf8", + timeout: timeoutMs, + // Suppress npm progress/warn output on stderr + stdio: ["ignore", "pipe", "pipe"], + }); // npm view --json emits a quoted string or null/empty for missing fields const trimmed = raw.trim(); if (!trimmed) { @@ -165,7 +161,11 @@ export function queryNpmRegistry(pkgName, timeoutMs = 8000) { // npm exits with code 1 when the package is NOT found ("E404") const stderr = err.stderr?.toString() || ""; const stdout = err.stdout?.toString() || ""; - if (stderr.includes("E404") || stdout.includes("E404") || stderr.includes("npm ERR! code E404")) { + if ( + stderr.includes("E404") || + stdout.includes("E404") || + stderr.includes("npm ERR! code E404") + ) { return { exists: false, createdMs: null }; } // Any other error (ETIMEDOUT, ENOTFOUND, etc.) = network/offline — return null diff --git a/scripts/check/check-duplication.mjs b/scripts/check/check-duplication.mjs index 1dc283a59c..60b5a7bf5c 100644 --- a/scripts/check/check-duplication.mjs +++ b/scripts/check/check-duplication.mjs @@ -15,13 +15,23 @@ const ROOT = process.cwd(); const BASELINE_PATH = path.resolve( process.argv.includes("--baseline") ? process.argv[process.argv.indexOf("--baseline") + 1] - : path.join(ROOT, "duplication-baseline.json") + : path.join(ROOT, "config/quality/duplication-baseline.json") ); const UPDATE = process.argv.includes("--update"); const EPS = 0.05; // tolerância de ruído de float (jscpd é determinístico; isto é margem) // Use local binary (pinned in package.json devDependencies — no registry download at CI time) const JSCPD_BIN = path.join(ROOT, "node_modules", ".bin", "jscpd"); -const JSCPD_FIXED_ARGS = ["src", "open-sse", "--reporters", "json", "--silent", "--min-tokens", "50", "--ignore", "**/*.test.ts,**/*.test.tsx,**/__tests__/**"]; +const JSCPD_FIXED_ARGS = [ + "src", + "open-sse", + "--reporters", + "json", + "--silent", + "--min-tokens", + "50", + "--ignore", + "**/*.test.ts,**/*.test.tsx,**/__tests__/**", +]; /** Avalia a % atual contra o baseline. */ export function evaluateDuplication(current, baseline, eps = EPS) { diff --git a/scripts/check/check-fabricated-docs.mjs b/scripts/check/check-fabricated-docs.mjs index 6bac4aa4d9..cedc675978 100644 --- a/scripts/check/check-fabricated-docs.mjs +++ b/scripts/check/check-fabricated-docs.mjs @@ -273,8 +273,8 @@ const ENV_VAR_DENYLIST = new Set([ // Gate allowlist constant names (JS identifiers, not env vars) — documented in // docs/architecture/QUALITY_GATES.md and docs/research/DISCOVERY_TOOL_DESIGN.md "KNOWN_STALE_DOC_REFS", // export const in check-docs-symbols.mjs - "KNOWN_MISSING", // export const in check-fetch-targets.mjs - "KNOWN_RAW_SQL", // export const in check-db-rules.mjs + "KNOWN_MISSING", // export const in check-fetch-targets.mjs + "KNOWN_RAW_SQL", // export const in check-db-rules.mjs ]); /** Endpoints that don't follow the standard route.ts pattern. */ @@ -313,6 +313,9 @@ const SKIP_DOC_FILES = new Set([ "docs/reference/PROVIDER_REFERENCE.md", // auto-generated from providers.ts "docs/reference/openapi.yaml", "docs/i18n", // translations — separate workflow + // Point-in-time documentation audit (v3.8.24): intentionally references drift, + // counts, and not-yet-existing files as part of documenting them — not living docs. + "docs/ops/DOCUMENTATION_AUDIT_REPORT.md", ]); // ── File discovery ───────────────────────────────────────────────────────── diff --git a/scripts/check/check-file-size.mjs b/scripts/check/check-file-size.mjs index eabc4c6807..82e4193a2a 100644 --- a/scripts/check/check-file-size.mjs +++ b/scripts/check/check-file-size.mjs @@ -15,7 +15,9 @@ function getArg(name, fallback) { const i = process.argv.indexOf(name); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : fallback; } -const BASELINE_PATH = path.resolve(getArg("--baseline", path.join(ROOT, "file-size-baseline.json"))); +const BASELINE_PATH = path.resolve( + getArg("--baseline", path.join(ROOT, "config/quality/file-size-baseline.json")) +); const UPDATE = process.argv.includes("--update"); const SCAN_DIRS = ["src", "open-sse", "electron", "bin"]; // Directories to skip when walking — build artifacts and installed packages. @@ -30,7 +32,8 @@ export function evaluateFileSizes(currentLocByFile, frozen, cap) { const improvements = []; for (const [file, loc] of Object.entries(currentLocByFile)) { if (file in frozen) { - if (loc > frozen[file]) violations.push(`${file}: ${loc} > congelado ${frozen[file]} (não pode crescer)`); + if (loc > frozen[file]) + violations.push(`${file}: ${loc} > congelado ${frozen[file]} (não pode crescer)`); else if (loc < frozen[file]) improvements.push([file, loc]); } else if (loc > cap) { violations.push(`${file}: ${loc} > cap ${cap} (arquivo novo acima do limite)`); @@ -49,7 +52,11 @@ function walk(dir, acc = []) { const p = path.join(dir, e.name); if (e.isDirectory()) { if (!SKIP_DIRS.has(e.name)) walk(p, acc); - } else if (/\.(ts|tsx)$/.test(e.name) && !/\.test\.tsx?$/.test(e.name) && !/\.d\.ts$/.test(e.name)) { + } else if ( + /\.(ts|tsx)$/.test(e.name) && + !/\.test\.tsx?$/.test(e.name) && + !/\.d\.ts$/.test(e.name) + ) { acc.push(p); } } @@ -59,7 +66,8 @@ function walk(dir, acc = []) { function collectLoc() { const out = {}; for (const d of SCAN_DIRS) - for (const f of walk(path.join(ROOT, d))) out[path.relative(ROOT, f).replace(/\\/g, "/")] = countLines(f); + for (const f of walk(path.join(ROOT, d))) + out[path.relative(ROOT, f).replace(/\\/g, "/")] = countLines(f); return out; } @@ -76,7 +84,8 @@ function main() { if (UPDATE && violations.length === 0 && improvements.length) { for (const [file, loc] of improvements) { - if (loc <= cap) delete frozen[file]; // caiu para dentro do cap → sai do baseline + if (loc <= cap) + delete frozen[file]; // caiu para dentro do cap → sai do baseline else frozen[file] = loc; // continua grande mas encolheu → trava no novo valor } baseline.frozen = Object.fromEntries(Object.entries(frozen).sort()); diff --git a/scripts/check/check-licenses.mjs b/scripts/check/check-licenses.mjs index 3061314db6..3c265144bc 100644 --- a/scripts/check/check-licenses.mjs +++ b/scripts/check/check-licenses.mjs @@ -22,7 +22,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; const ROOT = process.cwd(); -const ALLOWLIST_PATH = path.join(ROOT, ".license-allowlist.json"); +const ALLOWLIST_PATH = path.join(ROOT, "config/quality/.license-allowlist.json"); const CHECKER_BIN = path.join(ROOT, "node_modules", ".bin", "license-checker-rseidelsohn"); const VERBOSE = process.argv.includes("--verbose"); @@ -39,7 +39,9 @@ const PRINT_JSON = process.argv.includes("--json"); */ export function loadAllowlist() { if (!fs.existsSync(ALLOWLIST_PATH)) { - throw new Error(`Allowlist not found: ${ALLOWLIST_PATH}. Create .license-allowlist.json first.`); + throw new Error( + `Allowlist not found: ${ALLOWLIST_PATH}. Create .license-allowlist.json first.` + ); } const raw = fs.readFileSync(ALLOWLIST_PATH, "utf-8"); const parsed = JSON.parse(raw); @@ -86,7 +88,10 @@ export function classifyLicense(packageName, license, allowlist) { } // 4. Denied - return { status: "denied", reason: `license '${license}' not in allowlist and no exception registered for '${baseName}'` }; + return { + status: "denied", + reason: `license '${license}' not in allowlist and no exception registered for '${baseName}'`, + }; } /** @@ -195,7 +200,9 @@ function main() { // Print exceptions (informational) if (exceptions.length > 0) { - console.log("\n[check-licenses] Exceções registradas (não bloqueantes, revisar periodicamente):"); + console.log( + "\n[check-licenses] Exceções registradas (não bloqueantes, revisar periodicamente):" + ); for (const { pkgKey, license } of exceptions) { const baseName = stripVersion(pkgKey); const exc = allowlist.exceptions[baseName]; @@ -217,7 +224,9 @@ function main() { // Print violations and fail if (violations.length > 0) { - console.error("\n[check-licenses] ❌ VIOLAÇÕES DE POLÍTICA — deps de produção com licença não permitida:"); + console.error( + "\n[check-licenses] ❌ VIOLAÇÕES DE POLÍTICA — deps de produção com licença não permitida:" + ); for (const { pkgKey, license, reason } of violations) { console.error(` ✗ ${pkgKey}: ${license}`); console.error(` → ${reason}`); @@ -231,11 +240,14 @@ function main() { return; } - console.log("\n[check-licenses] ✅ Todos os pacotes de produção estão em conformidade com a política de licenças."); + console.log( + "\n[check-licenses] ✅ Todos os pacotes de produção estão em conformidade com a política de licenças." + ); } // Run only when invoked directly (not when imported by tests) -const isMain = process.argv[1] === pathToFileURL(import.meta.url).pathname || +const isMain = + process.argv[1] === pathToFileURL(import.meta.url).pathname || process.argv[1]?.endsWith("check-licenses.mjs"); if (isMain) { diff --git a/scripts/check/check-test-discovery.mjs b/scripts/check/check-test-discovery.mjs index 99948e6a2d..0d0f19d87b 100644 --- a/scripts/check/check-test-discovery.mjs +++ b/scripts/check/check-test-discovery.mjs @@ -34,7 +34,7 @@ const ROOT = process.cwd(); const BASELINE_PATH = path.resolve( process.argv.includes("--baseline") ? process.argv[process.argv.indexOf("--baseline") + 1] - : path.join(ROOT, "test-discovery-baseline.json") + : path.join(ROOT, "config/quality/test-discovery-baseline.json") ); const UPDATE = process.argv.includes("--update"); diff --git a/scripts/check/check-tracked-artifacts.mjs b/scripts/check/check-tracked-artifacts.mjs index 833ed8dc95..a0324fc168 100644 --- a/scripts/check/check-tracked-artifacts.mjs +++ b/scripts/check/check-tracked-artifacts.mjs @@ -15,7 +15,10 @@ import { execFileSync } from "node:child_process"; import { pathToFileURL } from "node:url"; const FORBIDDEN_PREFIXES = ["node_modules/", ".next/", "coverage/"]; -const FORBIDDEN_EXACT = new Set(["quality-metrics.json"]); +const FORBIDDEN_EXACT = new Set([ + "quality-metrics.json", // legacy root location (still forbidden if a stale run writes it) + "config/quality/quality-metrics.json", // current generated location (collect-metrics.mjs) +]); /** * Verifica se algum caminho na lista de arquivos rastreados corresponde a um @@ -80,7 +83,9 @@ function main() { process.exit(0); } - console.error(`[tracked-artifacts] FAIL — ${violations.length} forbidden artifact(s) tracked by git:`); + console.error( + `[tracked-artifacts] FAIL — ${violations.length} forbidden artifact(s) tracked by git:` + ); for (const v of violations) { console.error(` ✗ ${v}`); } diff --git a/scripts/check/check-type-coverage.mjs b/scripts/check/check-type-coverage.mjs index f6291c77ba..0051063fd3 100644 --- a/scripts/check/check-type-coverage.mjs +++ b/scripts/check/check-type-coverage.mjs @@ -33,7 +33,7 @@ const UPDATE = process.argv.includes("--update"); const BASELINE_PATH = path.resolve( process.argv.includes("--baseline") ? process.argv[process.argv.indexOf("--baseline") + 1] - : path.join(ROOT, "quality-baseline.json") + : path.join(ROOT, "config/quality/quality-baseline.json") ); // Small epsilon to absorb float noise between runs (type-coverage can vary ~0.01%). diff --git a/scripts/quality/check-quality-ratchet.mjs b/scripts/quality/check-quality-ratchet.mjs index 2e88519907..9948c5bb5a 100644 --- a/scripts/quality/check-quality-ratchet.mjs +++ b/scripts/quality/check-quality-ratchet.mjs @@ -12,8 +12,12 @@ function getArg(name, fallback) { const i = process.argv.indexOf(name); return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : fallback; } -const BASELINE = path.resolve(getArg("--baseline", path.join(cwd, "quality-baseline.json"))); -const METRICS = path.resolve(getArg("--metrics", path.join(cwd, "quality-metrics.json"))); +const BASELINE = path.resolve( + getArg("--baseline", path.join(cwd, "config/quality/quality-baseline.json")) +); +const METRICS = path.resolve( + getArg("--metrics", path.join(cwd, "config/quality/quality-metrics.json")) +); const SUMMARY = getArg("--summary", null); const UPDATE = process.argv.includes("--update"); // --allow-missing: pula métricas do baseline ausentes do metrics (em vez de falhar). @@ -73,7 +77,7 @@ for (const [key, spec] of Object.entries(baseline.metrics)) { status = "↑ melhorou"; if (REQUIRE_TIGHTEN && base - current > tightenSlack) { tightenFailures.push( - `${key}: melhorou de ${base} para ${current} (delta ${(base - current).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR`, + `${key}: melhorou de ${base} para ${current} (delta ${(base - current).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR` ); } } @@ -86,7 +90,7 @@ for (const [key, spec] of Object.entries(baseline.metrics)) { status = "↑ melhorou"; if (REQUIRE_TIGHTEN && current - base > tightenSlack) { tightenFailures.push( - `${key}: melhorou de ${base} para ${current} (delta ${(current - base).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR`, + `${key}: melhorou de ${base} para ${current} (delta ${(current - base).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR` ); } } @@ -99,10 +103,10 @@ const baselineKeys = new Set(Object.keys(baseline.metrics)); const orphans = Object.keys(metrics).filter((k) => !baselineKeys.has(k)); if (orphans.length > 0) { console.warn( - `[quality-ratchet] WARN: ${orphans.length} métrica(s) órfã(s) — presente(s) em ${path.basename(METRICS)} mas sem entrada no baseline: ${orphans.join(", ")}`, + `[quality-ratchet] WARN: ${orphans.length} métrica(s) órfã(s) — presente(s) em ${path.basename(METRICS)} mas sem entrada no baseline: ${orphans.join(", ")}` ); console.warn( - `[quality-ratchet] WARN: adicione ${orphans.length === 1 ? "essa métrica" : "essas métricas"} ao baseline (com value/direction) para que sejam catraceadas.`, + `[quality-ratchet] WARN: adicione ${orphans.length === 1 ? "essa métrica" : "essas métricas"} ao baseline (com value/direction) para que sejam catraceadas.` ); } @@ -140,7 +144,7 @@ if (failures.length) { if (REQUIRE_TIGHTEN && !UPDATE && tightenFailures.length > 0) { console.error( "[quality-ratchet] FALHOU (--require-tighten): métrica(s) melhoraram mas o baseline não foi apertado:\n" + - tightenFailures.map((f) => " ✗ " + f).join("\n"), + tightenFailures.map((f) => " ✗ " + f).join("\n") ); process.exit(1); } diff --git a/scripts/quality/collect-metrics.mjs b/scripts/quality/collect-metrics.mjs index bf24f96b29..2807409831 100644 --- a/scripts/quality/collect-metrics.mjs +++ b/scripts/quality/collect-metrics.mjs @@ -250,6 +250,9 @@ if (import.meta.url === pathToFileURL(process.argv[1] || "").href) { coverageByModule(); openapiCoverage(); await i18nUiCoverage(); - fs.writeFileSync(path.join(cwd, "quality-metrics.json"), JSON.stringify(out, null, 2) + "\n"); + fs.writeFileSync( + path.join(cwd, "config/quality/quality-metrics.json"), + JSON.stringify(out, null, 2) + "\n" + ); console.log("[collect-metrics]", JSON.stringify(out)); } diff --git a/tests/unit/build/check-tracked-artifacts.test.ts b/tests/unit/build/check-tracked-artifacts.test.ts index f3edfc83fd..99d8c05960 100644 --- a/tests/unit/build/check-tracked-artifacts.test.ts +++ b/tests/unit/build/check-tracked-artifacts.test.ts @@ -30,6 +30,11 @@ test("checkTrackedArtifacts: quality-metrics.json is flagged", () => { assert.equal(result.length, 1); }); +test("checkTrackedArtifacts: config/quality/quality-metrics.json is flagged", () => { + const result = checkTrackedArtifacts(["config/quality/quality-metrics.json"]); + assert.equal(result.length, 1); +}); + test("checkTrackedArtifacts: symlink mode (120000) is flagged", () => { const result = checkTrackedArtifacts([], ["node_modules"]); assert.equal(result.length, 1); diff --git a/tests/unit/check-deps.test.ts b/tests/unit/check-deps.test.ts index 864493efcc..6adaf44c48 100644 --- a/tests/unit/check-deps.test.ts +++ b/tests/unit/check-deps.test.ts @@ -18,17 +18,13 @@ test("no unapproved deps when all are allowlisted", () => { }); test("flags a dependency not on the allowlist (potential slopsquat)", () => { - assert.deepEqual( - findUnapprovedDeps(["react", "reactt-router"], new Set(["react"])), - ["reactt-router"] - ); + assert.deepEqual(findUnapprovedDeps(["react", "reactt-router"], new Set(["react"])), [ + "reactt-router", + ]); }); test("flags multiple new deps, preserves order, de-dupes", () => { - assert.deepEqual( - findUnapprovedDeps(["a", "b", "a", "c"], new Set(["a"])), - ["b", "c"] - ); + assert.deepEqual(findUnapprovedDeps(["a", "b", "a", "c"], new Set(["a"])), ["b", "c"]); }); // --- 6A.8: automatic workspace discovery --- @@ -64,7 +60,7 @@ test("6A.8: discoverManifests does NOT include node_modules, .next, or deep refe }); test("6A.8: all workspace package deps are in the allowlist (gate exits 0 with expanded scope)", () => { - const allowlistPath = path.join(repoRoot, "dependency-allowlist.json"); + const allowlistPath = path.join(repoRoot, "config/quality/dependency-allowlist.json"); const allowlist = new Set(JSON.parse(fs.readFileSync(allowlistPath, "utf8")).allowed || []); const manifests = discoverManifests(repoRoot); const allDeps: string[] = []; @@ -80,7 +76,11 @@ test("6A.8: all workspace package deps are in the allowlist (gate exits 0 with e ); } const unapproved = findUnapprovedDeps(allDeps, allowlist); - assert.deepEqual(unapproved, [], `expected all deps to be approved, got: ${unapproved.join(", ")}`); + assert.deepEqual( + unapproved, + [], + `expected all deps to be approved, got: ${unapproved.join(", ")}` + ); }); // --- 6A.8: stale-allowlist enforcement --- @@ -102,10 +102,9 @@ test("6A.8 stale: a dep removed from all manifests is detected as stale in allow test("7.8 evaluateDepAge: package older than 72h is OK", () => { const now = Date.now(); const createdMs = now - 73 * 60 * 60 * 1000; // 73 hours ago - const { ok, ageHours } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })( - createdMs, - now - ); + const { ok, ageHours } = ( + evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number } + )(createdMs, now); assert.ok(ok, "package older than 72h should be ok"); assert.ok(ageHours >= 73, "ageHours should reflect elapsed time"); }); @@ -113,20 +112,18 @@ test("7.8 evaluateDepAge: package older than 72h is OK", () => { test("7.8 evaluateDepAge: package exactly at 72h boundary is OK (boundary inclusive)", () => { const now = Date.now(); const createdMs = now - 72 * 60 * 60 * 1000; // exactly 72 hours ago - const { ok } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })( - createdMs, - now - ); + const { ok } = ( + evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number } + )(createdMs, now); assert.ok(ok, "package at exactly 72h should be ok (inclusive boundary)"); }); test("7.8 evaluateDepAge: package published 1h ago is NOT OK", () => { const now = Date.now(); const createdMs = now - 1 * 60 * 60 * 1000; // 1 hour ago - const { ok, ageHours } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })( - createdMs, - now - ); + const { ok, ageHours } = ( + evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number } + )(createdMs, now); assert.ok(!ok, "package published 1h ago should NOT be ok"); assert.ok(ageHours < 72, "ageHours should reflect < 72h"); }); @@ -135,28 +132,23 @@ test("7.8 evaluateDepAge: respects custom minAgeHours", () => { const now = Date.now(); const createdMs = now - 10 * 60 * 60 * 1000; // 10 hours ago // With 24h minimum, 10h-old package should fail - const { ok: failWith24 } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })( - createdMs, - now, - 24 - ); + const { ok: failWith24 } = ( + evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number } + )(createdMs, now, 24); assert.ok(!failWith24, "10h-old package should fail with 24h minimum"); // With 6h minimum, 10h-old package should pass - const { ok: passWith6 } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })( - createdMs, - now, - 6 - ); + const { ok: passWith6 } = ( + evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number } + )(createdMs, now, 6); assert.ok(passWith6, "10h-old package should pass with 6h minimum"); }); test("7.8 evaluateDepAge: future timestamp (time.created in future) is NOT OK", () => { const now = Date.now(); const createdMs = now + 1000; // 1s in future (clock skew edge case) - const { ok, ageHours } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })( - createdMs, - now - ); + const { ok, ageHours } = ( + evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number } + )(createdMs, now); assert.ok(!ok, "future timestamp should not be ok"); assert.ok(ageHours < 0, "ageHours should be negative for future timestamp"); }); @@ -169,11 +161,17 @@ test("7.8 evaluateDepAge: future timestamp (time.created in future) is NOT OK", // - any real npm package like "react" is found and old → does NOT appear in any list test("7.8 auditNewDepsRegistry: empty dep list returns all-empty results", () => { - const result = (auditNewDepsRegistry as (deps: string[], minAge?: number, now?: number) => { - notFound: string[]; - tooNew: Array<{ name: string; ageHours: number }>; - offline: string[]; - })([], 72, Date.now()); + const result = ( + auditNewDepsRegistry as ( + deps: string[], + minAge?: number, + now?: number + ) => { + notFound: string[]; + tooNew: Array<{ name: string; ageHours: number }>; + offline: string[]; + } + )([], 72, Date.now()); assert.deepEqual(result.notFound, []); assert.deepEqual(result.tooNew, []); assert.deepEqual(result.offline, []);