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.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-15 11:32:45 -03:00
committed by GitHub
parent 7eff73651a
commit 320a9d3f29
26 changed files with 303 additions and 211 deletions

7
.gitignore vendored
View File

@@ -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

View File

@@ -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/<domain>.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/<domain>.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 |
---

View File

@@ -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"
]
}

View File

@@ -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 = [

View File

@@ -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");

View File

@@ -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")
);
/**

View File

@@ -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

View File

@@ -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) {

View File

@@ -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 ─────────────────────────────────────────────────────────

View File

@@ -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());

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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}`);
}

View File

@@ -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%).

View File

@@ -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);
}

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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, []);