From e646413c7e5160f0c73e656a5a2430c692c99678 Mon Sep 17 00:00:00 2001 From: aarroyo Date: Fri, 21 Aug 2026 15:13:59 -0500 Subject: [PATCH 1/2] chore(repo): pin Node at the root, and drop a Sponsor button that leads nowhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #565, closes #563. **#565 — nothing enforced the Node version.** CI runs Node 20 and CONTRIBUTING said to pin it locally, but there was no `.nvmrc` and no root `engines`, so a contributor on 18 or 24 found out from a failing job. - `.nvmrc` with `20`, read by nvm, fnm, asdf and volta alike - root `engines: { "node": ">=20 <21" }`, the range the workflows actually use - `.nvmrc` added to the root-cleanliness allowlist, which is where the repository decides what may sit at the root -- the guard rejected it first, exactly as the issue said it would - the taxonomy's root policy updated in both languages to say the same, and three stale claims in that same section corrected while there: it still listed `DOCUMENTATION_VERSIONS.md` (no such file), `rulesets/` at the root (it moved under `src/`), and asserted "no application `src/` directories are maintained in this repository" -- which is now the opposite of true It warns, it does not block: `engine-strict` is off, verified against this machine's Node 24 -- `npm install` emits `EBADENGINE` and exits 0. The published CLI keeps `engines.node >= 18.0.0`, because that is a statement about consumers, not about building this repository. **#563 — the Sponsor button offered nothing.** The issue reported four empty keys; two of them did have values, so the file was checked rather than trusted: - `beyondnetcode` has no GitHub Sponsors listing (`hasSponsorsListing: false`), so `github: [beyondnetcode]` rendered a button leading to a page that does not exist - `sponsor:` is not a key GitHub's FUNDING.yml schema defines at all - `patreon:` and `open_collective:` are empty Nothing in the file was real, so the file is gone -- which is what the issue proposed for that case. Verified: root-cleanliness passes with `.nvmrc` present (46 entries read, 43 checked), guards 01 and 04 green. Co-Authored-By: Claude Opus 5 Signed-off-by: aarroyo --- .github/FUNDING.yml | 6 ------ .harness/scripts/ci/03-validate-root-cleanliness.mjs | 6 ++++++ .nvmrc | 1 + CONTRIBUTING.es.md | 2 +- CONTRIBUTING.md | 2 +- package.json | 3 +++ .../control-center/taxonomy/repository-taxonomy.es.md | 11 +++++------ .../control-center/taxonomy/repository-taxonomy.md | 11 +++++------ 8 files changed, 22 insertions(+), 20 deletions(-) delete mode 100644 .github/FUNDING.yml create mode 100644 .nvmrc diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 3e38e7b20..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,6 +0,0 @@ -github: - - beyondnetcode -patreon: -open_collective: -sponsor: - - beyondnetcode diff --git a/.harness/scripts/ci/03-validate-root-cleanliness.mjs b/.harness/scripts/ci/03-validate-root-cleanliness.mjs index b76259426..4f056ed7d 100755 --- a/.harness/scripts/ci/03-validate-root-cleanliness.mjs +++ b/.harness/scripts/ci/03-validate-root-cleanliness.mjs @@ -31,6 +31,12 @@ const allowedFiles = new Set([ "LICENSE", "CHANGELOG.md", ".editorconfig", + // #565: nvm, fnm, asdf and volta all read the version from the repository + // ROOT and take no flag to relocate it, so this one cannot live under + // `.harness/` either. It pins the same Node 20 the workflows use and the + // root `engines` range declares, so a contributor finds out from their tools + // rather than from a failing job. + ".nvmrc", ".gitignore", ".dockerignore", ".gitleaks.toml", diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..209e3ef4b --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/CONTRIBUTING.es.md b/CONTRIBUTING.es.md index b9dad1845..e1546eda2 100644 --- a/CONTRIBUTING.es.md +++ b/CONTRIBUTING.es.md @@ -25,7 +25,7 @@ Evolith es un **monorepo de npm workspaces** (`src/sdk/*`, `src/apps/*`, `src/pa ### A. Prerrequisitos -- **Node.js 20** es lo que ejecuta CI. El CLI declara `engines.node >= 18.0.0`, pero fija Node 20 localmente para coincidir con el pipeline. +- **Node.js 20** es lo que ejecuta CI, y `.nvmrc` lo fija — `nvm use` (o fnm/asdf/volta, que leen el mismo fichero) te deja en esa versión. El `package.json` de la raíz declara `engines.node >= 20 <21`, así que npm avisa si estás en otra en vez de que lo descubras por un job en rojo. Avisa, no bloquea: `engine-strict` está desactivado, así que otra versión de Node instala igual. El CLI publicado conserva `engines.node >= 18.0.0` — eso es lo que necesitan los *consumidores*, no lo que hace falta para construir este repositorio. - **npm** (compatible con workspaces; viene con Node). - **Git** con el modelo de ramificación GitFlow (ver [ADR-0050](./reference/core/architecture/adrs/core/0050-gitflow-branching-strategy.es.md)). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index adf286bb1..9f1e765a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,7 +25,7 @@ Evolith is an **npm workspaces monorepo** (`sdk/*`, `apps/*`, `src/packages/*`). ### A. Prerequisites -- **Node.js 20** is what CI runs. The CLI declares `engines.node >= 18.0.0`, but pin to Node 20 locally to match the pipeline. +- **Node.js 20** is what CI runs, and `.nvmrc` pins it — `nvm use` (or fnm/asdf/volta, all of which read the same file) puts you on it. The root `package.json` declares `engines.node >= 20 <21`, so npm warns if you are on anything else instead of letting you find out from a failing job. It warns rather than blocks: `engine-strict` is off, so a different Node still installs. The published CLI keeps `engines.node >= 18.0.0` — that is what *consumers* need, not what building this repository needs. - **npm** (workspaces-aware; ships with Node). - **Git** with the GitFlow branching model (see [ADR-0050](./reference/core/architecture/adrs/core/0050-gitflow-branching-strategy.md)). diff --git a/package.json b/package.json index f8e39f66c..be6bcb53e 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "src/apps/*", "src/packages/*" ], + "engines": { + "node": ">=20 <21" + }, "devDependencies": { "@commitlint/cli": "20.5.3", "@commitlint/config-conventional": "20.5.3", diff --git a/reference/core/control-center/taxonomy/repository-taxonomy.es.md b/reference/core/control-center/taxonomy/repository-taxonomy.es.md index 3f2d5775f..6fad93e6b 100644 --- a/reference/core/control-center/taxonomy/repository-taxonomy.es.md +++ b/reference/core/control-center/taxonomy/repository-taxonomy.es.md @@ -102,14 +102,13 @@ Este repositorio es propietario de la linea base arquitectonica y el mecanismo d La raiz debe mantenerse pequena y navegable. Las categorias permitidas son: -- Archivos publicos de navegacion y legales: `README.md`, `README.es.md`, `MASTER_INDEX.md`, `MASTER_INDEX.es.md`, `DOCUMENTATION_VERSIONS.md`, `DOCUMENTATION_VERSIONS.es.md`, `AGENTS.md`, `AGENTS.es.md` y `LICENSE`. -- Dot-folders de tooling y plataforma: `.github/`, `.harness/`, `.husky/`, `.vscode/`, `.bmad-core/`, `.mimocode/`, `.claude/`, `.obsidian/`, y configuracion de editores o automatizacion (`.editorconfig`, `.gitignore`, `.markdownlint.json`). +- Archivos publicos de navegacion y legales: `README.md`, `README.es.md`, `MASTER_INDEX.md`, `MASTER_INDEX.es.md`, `AGENTS.md`, `AGENTS.es.md`, `CONTRIBUTING`, `SECURITY`, `CODE_OF_CONDUCT` (cada uno bilingue), `CHANGELOG.md` y `LICENSE`. +- Dot-folders de tooling y plataforma: `.github/`, `.harness/`, `.husky/`, `.vscode/`, `.bmad-core/`, `.mimocode/`, `.claude/`, `.obsidian/`, y configuracion de editores o automatizacion (`.editorconfig`, `.gitignore`, `.markdownlint.json`, `.nvmrc`). +- **Pines de toolchain que se leen desde la raiz:** `.nvmrc` fija la version de Node que nvm, fnm, asdf y volta leen de la raiz del repositorio y a las que no se les puede indicar otra ubicacion. Declara el mismo Node 20 que usan los workflows y que declara el rango `engines` de la raiz. - **Convencion de carpetas de herramientas:** Cada herramienta de IA/IDE/autoria obtiene su propia carpeta con punto en la raiz del repositorio (`.claude/`, `.mimocode/`, `.obsidian/`, `.vscode/`). No se pueden anidar dentro de una carpeta padre porque cada runtime espera su configuracion en la raiz del workspace. NO crear carpetas de agrupacion como `.setup/` o similares — los contratos de las herramientas requieren ubicacion en la raiz. - `reference/` para el corpus documental y arquitectonico. -- `src/sdk/` para tooling de acceso ejecutable, CLI y MCP. -- `rulesets/` para reglas de gobernanza legibles por maquina, incluyendo `src/rulesets/topologies/` para reglas ejecutables especificas por topologia. - -No se mantienen directorios `src/` de aplicaciones en este repositorio; la implementacion ejecutable pertenece a UMS o a otro repositorio de producto con alcance explicito. +- `src/` para todos los workspaces ejecutables: `src/sdk/` (CLI y tooling de acceso), `src/packages/`, `src/apps/`, `src/tests/`, y `src/rulesets/` para reglas de gobernanza legibles por maquina incluyendo `src/rulesets/topologies/`. +- `product/` para el corpus de producto, y `docs/` para guias de lectura y evidencia publicada. El directorio `/topologies/` en la raiz queda explicitamente prohibido. La gobernanza multi-topologia no crea una nueva area de contenido en la raiz del repositorio; debe permanecer dentro de los limites de autoridad existentes establecidos por [ADR-0048](../../architecture/adrs/core/0048-enterprise-taxonomy-reference-layout.es.md), [ADR-0070](../../architecture/adrs/core/0070-lean-root-repository-taxonomy.es.md) y [ADR-0079](../../architecture/adrs/core/0079-multi-topology-reference-corpus.es.md). Cualquier propuesta futura para crear `/topologies/` en la raiz del repositorio requiere un ADR reemplazante aceptado que modifique la taxonomia de raiz, actualice este estandar, actualice `src/rulesets/cross-cutting/repository-taxonomy.rules.json`, actualice `src/rulesets/opa/taxonomy.rego` y actualice `.harness/scripts/ci/03-validate-root-cleanliness.mjs` en el mismo cambio. diff --git a/reference/core/control-center/taxonomy/repository-taxonomy.md b/reference/core/control-center/taxonomy/repository-taxonomy.md index 01f960a68..a8f8167cb 100644 --- a/reference/core/control-center/taxonomy/repository-taxonomy.md +++ b/reference/core/control-center/taxonomy/repository-taxonomy.md @@ -102,14 +102,13 @@ This repository owns the architectural baseline and promotion mechanism. A produ The root should be kept small and navigable. Permitted categories are: -- Public navigation and legal files: `README.md`, `README.es.md`, `MASTER_INDEX.md`, `MASTER_INDEX.es.md`, `DOCUMENTATION_VERSIONS.md`, `DOCUMENTATION_VERSIONS.es.md`, `AGENTS.md`, `AGENTS.es.md` and `LICENSE`. -- Tooling and platform dot-folders: `.github/`, `.harness/`, `.husky/`, `.vscode/`, `.bmad-core/`, `.mimocode/`, `.claude/`, `.obsidian/`, and editor or automation configuration (`.editorconfig`, `.gitignore`, `.markdownlint.json`). +- Public navigation and legal files: `README.md`, `README.es.md`, `MASTER_INDEX.md`, `MASTER_INDEX.es.md`, `AGENTS.md`, `AGENTS.es.md`, `CONTRIBUTING`, `SECURITY`, `CODE_OF_CONDUCT` (each bilingual), `CHANGELOG.md` and `LICENSE`. +- Tooling and platform dot-folders: `.github/`, `.harness/`, `.husky/`, `.vscode/`, `.bmad-core/`, `.mimocode/`, `.claude/`, `.obsidian/`, and editor or automation configuration (`.editorconfig`, `.gitignore`, `.markdownlint.json`, `.nvmrc`). +- **Toolchain pins read from the root:** `.nvmrc` pins the Node version that nvm, fnm, asdf and volta all read from the repository root and cannot be told to look for elsewhere. It states the same Node 20 the workflows use and the root `engines` range declares. - **Tool folder convention:** Each AI/IDE/authoring tool gets its own dot-folder at repository root (`.claude/`, `.mimocode/`, `.obsidian/`, `.vscode/`). These cannot be nested inside a parent folder because each tool's runtime expects its configuration at the workspace root. Do NOT create `.setup/` or similar grouping folders — tool contracts require root-level placement. - `reference/` for the documentary and architectural corpus. -- `src/sdk/` for CLI, MCP, and executable access tooling. -- `rulesets/` for machine-readable governance rules, including `src/rulesets/topologies/` for topology-specific executable rules. - -No application `src/` directories are maintained in this repository; executable implementation belongs to UMS or another product repository with explicit scope. +- `src/` for every executable workspace: `src/sdk/` (CLI and access tooling), `src/packages/`, `src/apps/`, `src/tests/`, and `src/rulesets/` for machine-readable governance rules including `src/rulesets/topologies/`. +- `product/` for the product corpus, and `docs/` for reader-facing guides and published evidence. Root-level `/topologies/` is explicitly prohibited. Multi-topology governance does not create a new repository-root content area; it must remain inside the existing authority boundaries established by [ADR-0048](../../architecture/adrs/core/0048-enterprise-taxonomy-reference-layout.md), [ADR-0070](../../architecture/adrs/core/0070-lean-root-repository-taxonomy.md), and [ADR-0079](../../architecture/adrs/core/0079-multi-topology-reference-corpus.md). Any future proposal to create `/topologies/` at the repository root requires a superseding accepted ADR that amends the root taxonomy, updates this standard, updates `src/rulesets/cross-cutting/repository-taxonomy.rules.json`, updates `src/rulesets/opa/taxonomy.rego`, and updates `.harness/scripts/ci/03-validate-root-cleanliness.mjs` in the same change. From b5998c60053b88d3bad49cc79ab4b12d6e72f241 Mon Sep 17 00:00:00 2001 From: aarroyo Date: Fri, 21 Aug 2026 15:18:49 -0500 Subject: [PATCH 2/2] docs(gaps): record that GT-555's FUNDING.yml evidence was superseded, not lost `08-validate-tracking.mjs` caught the deletion immediately: GT-555 closed on six evidence artifacts and one of them was `.github/FUNDING.yml`, which #563 removed. The guard is right to refuse -- a closed gap pointing at a file that no longer exists is a closure nobody can re-check. Rather than repoint or quietly drop it: - the artifact leaves GT-555's evidence list, and the record says in its own words WHY, so the removal does not read as an omission - the acceptance criterion in both catalog halves is KEPT and annotated as superseded, because it was satisfied when it was written. Rendering was never the problem; what it rendered was. The other five artifacts are untouched and still carry the closure. Verified: `08-validate-tracking.mjs` passes. Co-Authored-By: Claude Opus 5 Signed-off-by: aarroyo --- .../core/control-center/evidence/gap-closure-evidence.json | 4 ++-- .../core/control-center/gaps/gap-reference-catalog.es.md | 2 +- reference/core/control-center/gaps/gap-reference-catalog.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/reference/core/control-center/evidence/gap-closure-evidence.json b/reference/core/control-center/evidence/gap-closure-evidence.json index f5068c7d5..83c449f63 100644 --- a/reference/core/control-center/evidence/gap-closure-evidence.json +++ b/reference/core/control-center/evidence/gap-closure-evidence.json @@ -7855,14 +7855,14 @@ ".github/ISSUE_TEMPLATE/config.yml", ".github/ISSUE_TEMPLATE/bug-report.yml", ".github/ISSUE_TEMPLATE/feature-request.yml", - ".github/FUNDING.yml", ".github/workflows/sdk-cli-release.yml" ], "validationCommands": [ "Added `.github/CODEOWNERS`, `.github/ISSUE_TEMPLATE/config.yml` (blank issues off; contact links to Discussions, the private security advisory and CONTRIBUTING), and bug-report and feature-request templates written in plain language. `FUNDING.yml` moved from `src/sdk/cli/` to `.github/` so GitHub renders it.", "Near-miss recorded: CODEOWNERS first shipped naming `@beyondnetcode/evolith-team`, copied from the release workflow's failure notifier. That team DOES NOT EXIST -- `gh api orgs/beyondnetcode/teams` returns only `evolith-core-s-development`.", "Why that mattered: GitHub silently ignores an unresolvable owner, so every CODEOWNERS line would have routed nothing while the file looked correct.", - "Corrected to the verified team, which was also confirmed to have repository access. The same bad handle in `.github/workflows/sdk-cli-release.yml` was fixed, which means that failure notification had been at-mentioning nobody." + "Corrected to the verified team, which was also confirmed to have repository access. The same bad handle in `.github/workflows/sdk-cli-release.yml` was fixed, which means that failure notification had been at-mentioning nobody.", + "2026-08-21 (#563): `.github/FUNDING.yml` removed from this evidence list because the file was deleted. It rendered, which is what this gap asked for; what it rendered was four keys that led nowhere -- `beyondnetcode` has no GitHub Sponsors listing (`hasSponsorsListing: false`), `sponsor:` is not a key GitHub's schema defines, and `patreon:`/`open_collective:` were empty. The remaining five artifacts are untouched and still carry this closure." ], "dependencyDisposition": "none", "dependencyRationale": "The gap has no dependencies: the files are self-contained GitHub community-health files. It feeds the GitHub bridge deliverable of UP-003, but nothing had to land first for these to take effect." diff --git a/reference/core/control-center/gaps/gap-reference-catalog.es.md b/reference/core/control-center/gaps/gap-reference-catalog.es.md index 613e3036c..8eebe3aa4 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.es.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.es.md @@ -6814,7 +6814,7 @@ Serie histórica de gaps registrada en el antiguo `gap-analysis-core.es.md`, pre - **Criterios de aceptación:** - [x] Existe `CODEOWNERS` y enruta la revisión de las áreas principales -- `.github/CODEOWNERS`. _(commit `9a13d0d6`)_ - [x] Se puede registrar un bug y una solicitud de funcionalidad desde el selector de issues -- `.github/ISSUE_TEMPLATE/bug-report.yml` y `feature-request.yml`, en lenguaje llano, más `config.yml` que desactiva los issues en blanco y ofrece enlaces de contacto a Discussions, el aviso privado de seguridad y CONTRIBUTING. _(commit `9a13d0d6`)_ - - [x] `FUNDING.yml` se renderiza en la página del repositorio -- movido de `src/sdk/cli/` a `.github/`. _(commit `9a13d0d6`)_ + - [x] `FUNDING.yml` se renderiza en la página del repositorio -- movido de `src/sdk/cli/` a `.github/`. _(commit `9a13d0d6`)_ **Superado el 2026-08-21 (#563):** el fichero fue eliminado, y este criterio se conserva en vez de reescribirse porque estaba satisfecho cuando se escribió. El problema nunca fue que se renderizara, sino lo que renderizaba. `beyondnetcode` no tiene ficha de GitHub Sponsors (`hasSponsorsListing: false`), así que la clave `github:` producía un botón Sponsor que llevaba a una página inexistente; `sponsor:` no es una clave que defina el esquema de GitHub; `patreon:` y `open_collective:` estaban vacías. Un FUNDING.yml ausente es honesto donde uno que se renderiza y no ofrece nada no lo es. - **Dependencias:** Alimenta el entregable de puente a GitHub de [UP-003](../opportunities/UP-003-user-contribution-intake-mechanism.es.md). - **Cierre (2026-07-18, commit `9a13d0d6`):** La superficie de colaboración es usable desde fuera del equipo core. _Casi-fallo registrado:_ CODEOWNERS salió primero nombrando `@beyondnetcode/evolith-team`, copiado del notificador de fallos del workflow de release -- ese equipo NO EXISTE (`gh api orgs/beyondnetcode/teams` devuelve solo `evolith-core-s-development`). GitHub ignora en silencio un owner que no resuelve, así que cada línea habría enrutado a nadie mientras el archivo parecía correcto. Corregido al equipo verificado, que además se confirmó con acceso al repositorio; el mismo handle erróneo en `sdk-cli-release.yml` fue arreglado, lo que significa que esa notificación de fallo llevaba mencionando a nadie. - **Estado:** `COMPLETADO` diff --git a/reference/core/control-center/gaps/gap-reference-catalog.md b/reference/core/control-center/gaps/gap-reference-catalog.md index 900ed2a48..d554cebca 100644 --- a/reference/core/control-center/gaps/gap-reference-catalog.md +++ b/reference/core/control-center/gaps/gap-reference-catalog.md @@ -6909,7 +6909,7 @@ Historical gap series tracked in the former `gap-analysis-core.md`, preserved fo - **Acceptance criteria:** - [x] `CODEOWNERS` exists and routes review for the main areas -- `.github/CODEOWNERS`. _(commit `9a13d0d6`)_ - [x] A bug report and a feature request can be filed from the issue chooser -- `.github/ISSUE_TEMPLATE/bug-report.yml` and `feature-request.yml`, in plain language, plus `config.yml` turning blank issues off and offering contact links to Discussions, the private security advisory and CONTRIBUTING. _(commit `9a13d0d6`)_ - - [x] `FUNDING.yml` renders on the repository page -- moved from `src/sdk/cli/` to `.github/`. _(commit `9a13d0d6`)_ + - [x] `FUNDING.yml` renders on the repository page -- moved from `src/sdk/cli/` to `.github/`. _(commit `9a13d0d6`)_ **Superseded 2026-08-21 (#563):** the file was deleted, and this criterion is kept rather than rewritten because it was satisfied when it was written. Rendering was never the problem; what it rendered was. `beyondnetcode` has no GitHub Sponsors listing (`hasSponsorsListing: false`), so the `github:` key produced a Sponsor button leading to a page that does not exist; `sponsor:` is not a key GitHub's schema defines; `patreon:` and `open_collective:` were empty. An absent FUNDING.yml is honest where a rendered one offering nothing is not. - **Dependencies:** Feeds the GitHub bridge deliverable of [UP-003](../opportunities/UP-003-user-contribution-intake-mechanism.md). - **Closure (2026-07-18, commit `9a13d0d6`):** The collaboration surface is usable from outside the core team. _Near-miss recorded:_ CODEOWNERS first shipped naming `@beyondnetcode/evolith-team`, copied from the release workflow's failure notifier -- that team DOES NOT EXIST (`gh api orgs/beyondnetcode/teams` returns only `evolith-core-s-development`). GitHub silently ignores an unresolvable owner, so every line would have routed nothing while the file looked correct. Corrected to the verified team, which was also confirmed to have repository access; the same bad handle in `sdk-cli-release.yml` was fixed, meaning that failure notification had been at-mentioning nobody. - **Status:** `DONE`