From dd0bd89dda3aa2620e6be2e9a6eb1d4c25e3888c Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sun, 24 May 2026 21:56:40 -0400 Subject: [PATCH 1/7] Add Vega project inventory, schema, and validation CI --- .github/workflows/check-project-inventory.yml | 32 ++ .gitignore | 1 + PROJECT_STATUS.md | 50 +++ package-lock.json | 288 ++++++++++++++++++ package.json | 14 + project-inventory.schema.yml | 150 +++++++++ project-inventory.yml | 224 ++++++++++++++ scripts/check-project-inventory.js | 168 ++++++++++ 8 files changed, 927 insertions(+) create mode 100644 .github/workflows/check-project-inventory.yml create mode 100644 .gitignore create mode 100644 PROJECT_STATUS.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 project-inventory.schema.yml create mode 100644 project-inventory.yml create mode 100644 scripts/check-project-inventory.js diff --git a/.github/workflows/check-project-inventory.yml b/.github/workflows/check-project-inventory.yml new file mode 100644 index 0000000..c976e8f --- /dev/null +++ b/.github/workflows/check-project-inventory.yml @@ -0,0 +1,32 @@ +name: Check project inventory + +on: + pull_request: + paths: + - project-inventory.yml + - project-inventory.schema.yml + - PROJECT_STATUS.md + - scripts/check-project-inventory.js + - package.json + - package-lock.json + - .github/workflows/check-project-inventory.yml + schedule: + - cron: '0 12 * * 1' + workflow_dispatch: + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: npm ci + - run: node scripts/check-project-inventory.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_ORG: vega diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md new file mode 100644 index 0000000..92a8e68 --- /dev/null +++ b/PROJECT_STATUS.md @@ -0,0 +1,50 @@ +# Project Status Definitions + +This document defines public metadata values used by [`project-inventory.yml`](./project-inventory.yml). + +Field usage: + +- `support_level` is optional; default inference is `active -> full`, `experimental -> best_effort`, `maintenance -> best_effort`, `deprecated -> none`, `archived -> none`. +- `replacement` is optional and should be included when a clear migration target exists. +- `notes` is optional and should be used sparingly for exceptions that are not captured by enum fields. +- `uncategorized_repos` is a holding list for known public repos that are intentionally not fully classified yet (often lightly maintained, historical, or low-signal repos). +- `core_maintainers` is optional and should be used when a repository has clear, consistent maintainer leadership. +- `research_prototypes` groups research repositories that share common lifecycle/support/ownership defaults to reduce repetitive metadata. + +## `lifecycle_status` + +| Value | Meaning | +| --- | --- | +| `active` | Normal development. Issues, pull requests, and releases are expected. | +| `experimental` | Exploratory work. APIs, scope, or long-term maintenance may change. | +| `maintenance` | Kept working, but limited new feature development. | +| `deprecated` | Users should migrate elsewhere. A replacement should be listed where possible. | +| `archived` | Historical or read-only project. No support is expected. | + +## `support_level` + +| Value | Meaning | +| --- | --- | +| `full` | Maintainers expect to review issues/security reports and publish releases as appropriate. | +| `best_effort` | Maintained when time allows; no strong response commitment. | +| `none` | Historical or unsupported; no maintainer response should be expected. | + +## `project_role` + +| Value | Meaning | +| --- | --- | +| `primary` | Main Vega/Altair library, runtime, grammar, service, schema, editor, or public-facing site. | +| `ecosystem` | Important package, helper, extension, dataset, or ecosystem utility. | +| `integration` | Framework, platform, package-manager, documentation, or external-system integration. | +| `example` | Tutorial, demo, sample, benchmark, or teaching material. | +| `research` | Research prototype, exploratory system, proposal, or academic project. | +| `infrastructure` | Build, deployment, governance, CI, org metadata, or operational tooling. | +| `historical` | Superseded, archived, version-specific, or retained for historical reference. | + +## `owner_model` + +| Value | Meaning | +| --- | --- | +| `org_shared` | The Vega maintainer group collectively owns the repository. | +| `named_lead` | We have a primary maintainer for triage, release, or review leadership | +| `unowned` | No active maintainer ownership; generally reserved for archived or unsupported repositories. | diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..dd06839 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,288 @@ +{ + "name": "vega-org-metadata", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vega-org-metadata", + "version": "1.0.0", + "dependencies": { + "@octokit/rest": "^22.0.1", + "ajv": "^8.17.1", + "yaml": "^2.8.1" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", + "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.9.tgz", + "integrity": "sha512-o8Bi3f608eyM+7BmBiUWxFsdjLb3/ym1cQek5LZOv9KkZcxRrHCPhhRzm6xjO6HVZ85ItD6+sTsjxo821SVa/A==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.3", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "content-type": "^2.0.0", + "fast-content-type-parse": "^3.0.0", + "json-with-bigint": "^3.5.3", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-with-bigint": { + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz", + "integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==", + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d0e7f0a --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "vega-org-metadata", + "private": true, + "version": "1.0.0", + "description": "Validation tooling for Vega GitHub org project inventory", + "scripts": { + "check:project-inventory": "node scripts/check-project-inventory.js" + }, + "dependencies": { + "@octokit/rest": "^22.0.1", + "ajv": "^8.17.1", + "yaml": "^2.8.1" + } +} diff --git a/project-inventory.schema.yml b/project-inventory.schema.yml new file mode 100644 index 0000000..40da304 --- /dev/null +++ b/project-inventory.schema.yml @@ -0,0 +1,150 @@ +$schema: https://json-schema.org/draft/2020-12/schema +type: object +required: + - metadata_version + - repos +properties: + metadata_version: + type: integer + definitions: + type: string + schema: + type: string + # Known public repositories that are intentionally not fully classified yet. + uncategorized_repos: + type: array + items: + type: string + # Primary categorized repositories. support_level is optional and may be inferred + # from lifecycle_status by validator defaults. + repos: + type: array + items: + type: object + additionalProperties: false + required: + - name + - lifecycle_status + - project_role + - owner_model + properties: + name: + type: string + lifecycle_status: + enum: + - active + - experimental + - maintenance + - deprecated + - archived + support_level: + enum: + - full + - best_effort + - none + project_role: + enum: + - primary + - ecosystem + - integration + - example + - research + - infrastructure + - historical + owner_model: + enum: + - org_shared + - named_lead + - unowned + replacement: + type: string + notes: + type: string + repo_lead: + type: + - string + - 'null' + backup: + type: + - string + - 'null' + package_names: + type: array + items: + type: string + core_maintainers: + type: array + items: + type: string + # Research repos grouped with shared defaults to avoid repetitive metadata. + research_prototypes: + type: object + required: + - defaults + - repos + additionalProperties: false + properties: + defaults: + type: object + additionalProperties: false + required: + - lifecycle_status + - project_role + - owner_model + properties: + lifecycle_status: + enum: + - active + - experimental + - maintenance + - deprecated + - archived + support_level: + enum: + - full + - best_effort + - none + project_role: + enum: + - research + owner_model: + enum: + - org_shared + - named_lead + - unowned + repos: + type: array + items: + oneOf: + - type: string + - type: object + additionalProperties: false + required: + - name + properties: + name: + type: string + lifecycle_status: + enum: + - active + - experimental + - maintenance + - deprecated + - archived + support_level: + enum: + - full + - best_effort + - none + project_role: + enum: + - research + owner_model: + enum: + - org_shared + - named_lead + - unowned + core_maintainers: + type: array + items: + type: string diff --git a/project-inventory.yml b/project-inventory.yml new file mode 100644 index 0000000..ac4162b --- /dev/null +++ b/project-inventory.yml @@ -0,0 +1,224 @@ +metadata_version: 1 +definitions: ./PROJECT_STATUS.md +schema: ./project-inventory.schema.yml +repos: + - name: .github + lifecycle_status: active + project_role: infrastructure + owner_model: org_shared + - name: altair + lifecycle_status: active + project_role: primary + owner_model: org_shared + core_maintainers: + - joelostblom + - mattijn + - name: dash-vega-components + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: editor + lifecycle_status: active + project_role: primary + owner_model: named_lead + core_maintainers: + - domoritz + - hydrosquall + repo_lead: domoritz + - name: editor-backend + lifecycle_status: maintenance + project_role: primary + owner_model: org_shared + - name: react-vega + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: roadmap + lifecycle_status: maintenance + project_role: infrastructure + owner_model: org_shared + - name: schema + lifecycle_status: active + project_role: primary + owner_model: org_shared + - name: sphinxext-altair + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: svelte-vega + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: ts-json-schema-generator + lifecycle_status: active + support_level: best_effort + project_role: infrastructure + owner_model: org_shared + - name: vega + lifecycle_status: active + project_role: primary + owner_model: named_lead + core_maintainers: + - hydrosquall + repo_lead: hydrosquall + - name: vega-datasets + lifecycle_status: maintenance + project_role: ecosystem + owner_model: named_lead + core_maintainers: + - dsmedia + repo_lead: dsmedia + - name: vega-embed + lifecycle_status: active + project_role: primary + owner_model: org_shared + - name: vega-lite + lifecycle_status: active + project_role: primary + owner_model: named_lead + repo_lead: domoritz + core_maintainers: + - domoritz + - name: vega-lite-api + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: vega-loader-arrow + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: vega-loader-parquet + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared + - name: vega-themes + lifecycle_status: maintenance + project_role: ecosystem + owner_model: org_shared + - name: vega-tooltip + lifecycle_status: maintenance + project_role: ecosystem + owner_model: org_shared + - name: vega.github.io + lifecycle_status: active + project_role: primary + owner_model: org_shared + - name: vegafusion + lifecycle_status: active + project_role: ecosystem + owner_model: named_lead + core_maintainers: + - jonmmease + repo_lead: jonmmease + - name: vl-convert + lifecycle_status: active + project_role: ecosystem + owner_model: named_lead + core_maintainers: + - jonmmease + repo_lead: jonmmease + - name: vl-convert-service + lifecycle_status: maintenance + project_role: integration + owner_model: named_lead + core_maintainers: + - jonmmease + repo_lead: jonmmease + - name: vue-vega + lifecycle_status: maintenance + project_role: integration + owner_model: org_shared +uncategorized_repos: + - altair_ally + - altair_tiles + - compass + - compassql-analysis + - dataflow-api + - dataflow-vis + - datalib + - datalib-sketch + - ipyvega + - logos + - polestar + - react-vega-lite + - scalable-vega + - SciPy2024-Altair-Tutorial + - ts-api + - vega-benchmarks + - vega-bundler + - vega-canvas + - vega-crossfilter + - vega-dataflow + - vega-dataflow-examples + - vega-desktop + - vega-editor + - vega-embed-v2 + - vega-encode + - vega-event-selector + - vega-expression + - vega-force + - vega-geo + - vega-gist + - vega-hierarchy + - vega-label + - vega-lib + - vega-lite-dev-config + - vega-lite-params-proposal + - vega-lite-pr-previews + - vega-lite-shorthand + - vega-lite-to-api + - vega-lite-transforms2sql + - vega-lite-tutorials + - vega-lite-ui + - vega-lite-v1 + - vega-lite-v2 + - vega-lite-v3 + - vega-lite-v4 + - vega-loader + - vega-logging + - vega-observable + - vega-parser + - vega-plus + - vega-projection + - vega-projection-extended + - vega-render-service + - vega-renderer-webgl + - vega-runtime + - vega-scale + - vega-scenegraph + - vega-statistics + - vega-tile + - vega-transforms + - vega-tutorials + - vega-typings + - vega-util + - vega-view + - vega-view-transforms + - vega-voronoi + - vega-vscode + - vega-webgl-renderer + - vega-webgpu + - vega-wordcloud + - vega2-docs + - vegafusion-conda-feedstock + - vegafusion-conda-jupyter-feedstock + - vegafusion-conda-python-embed-feedstock + - vegafusion-demos + - vegafusion-logos + - vg-transforms2sql + - vl-convert-python-feedstock + - web-service +research_prototypes: + defaults: + lifecycle_status: maintenance + project_role: research + owner_model: org_shared + repos: + - compassql + - falcon + - lyra + - voyager + - voyager-docs + - voyager-electron + - voyager-server + - voyager2 diff --git a/scripts/check-project-inventory.js b/scripts/check-project-inventory.js new file mode 100644 index 0000000..0da25c9 --- /dev/null +++ b/scripts/check-project-inventory.js @@ -0,0 +1,168 @@ +#!/usr/bin/env node + +const fs = require('node:fs/promises'); +const path = require('node:path'); +const process = require('node:process'); +const YAML = require('yaml'); +const Ajv2020 = require('ajv/dist/2020'); +const { Octokit } = require('@octokit/rest'); + +const ROOT = process.cwd(); +const INVENTORY_PATH = path.join(ROOT, 'project-inventory.yml'); +const SCHEMA_PATH = path.join(ROOT, 'project-inventory.schema.yml'); +const SUPPORT_DEFAULTS = { + active: 'full', + experimental: 'best_effort', + maintenance: 'best_effort', + deprecated: 'none', + archived: 'none', +}; + +function formatAjvErrors(errors = []) { + return errors.map((err) => { + const loc = err.instancePath || '/'; + return `- ${loc} ${err.message}`; + }); +} + +async function readYaml(filePath) { + const raw = await fs.readFile(filePath, 'utf8'); + return YAML.parse(raw); +} + +function warn(msg) { + console.warn(`[warn] ${msg}`); +} + +function fail(msg) { + console.error(`[error] ${msg}`); +} + +function supportLevelFor(entry) { + return entry.support_level || SUPPORT_DEFAULTS[entry.lifecycle_status]; +} + +async function fetchPublicRepos(org, token) { + const octokit = new Octokit({ auth: token }); + const repos = await octokit.paginate(octokit.repos.listForOrg, { + org, + type: 'public', + per_page: 100, + }); + return repos; +} + +async function main() { + const inventory = await readYaml(INVENTORY_PATH); + const schema = await readYaml(SCHEMA_PATH); + + const ajv = new Ajv2020({ allErrors: true, strict: false }); + const validate = ajv.compile(schema); + const valid = validate(inventory); + + if (!valid) { + fail('Inventory failed schema validation.'); + for (const err of formatAjvErrors(validate.errors)) { + fail(err); + } + process.exit(1); + } + + const repoEntries = inventory.repos || []; + const researchDefaults = inventory.research_prototypes?.defaults || null; + const researchEntriesRaw = inventory.research_prototypes?.repos || []; + const researchEntries = researchEntriesRaw.map((entry) => { + if (typeof entry === 'string') { + return { ...(researchDefaults || {}), name: entry }; + } + return { ...(researchDefaults || {}), ...entry }; + }); + const allEntries = [...repoEntries, ...researchEntries]; + const uncategorized = new Set(inventory.uncategorized_repos || []); + const inventoryByName = new Map(); + let hasErrors = false; + + for (const entry of allEntries) { + if (inventoryByName.has(entry.name)) { + fail(`Duplicate inventory entry for repository '${entry.name}'.`); + hasErrors = true; + continue; + } + inventoryByName.set(entry.name, entry); + + const effectiveSupportLevel = supportLevelFor(entry); + + if (entry.lifecycle_status === 'archived' && effectiveSupportLevel !== 'none') { + fail(`'${entry.name}' is archived but support_level is not 'none'.`); + hasErrors = true; + } + + if (entry.owner_model === 'named_lead' && !entry.repo_lead) { + fail(`'${entry.name}' uses owner_model 'named_lead' but repo_lead is missing.`); + hasErrors = true; + } + + if (entry.owner_model === 'unowned') { + const hasSupportNone = effectiveSupportLevel === 'none'; + const hasClarifyingNote = typeof entry.notes === 'string' && entry.notes.trim().length > 0; + if (!hasSupportNone && !hasClarifyingNote) { + warn(`'${entry.name}' is 'unowned' but neither support_level:none nor clarifying notes were provided.`); + } + } + + if ( + (entry.lifecycle_status === 'deprecated' || entry.lifecycle_status === 'archived') && + !entry.replacement + ) { + warn(`'${entry.name}' is ${entry.lifecycle_status} and has no replacement set. Verify this is intentional.`); + } + } + + const token = process.env.GITHUB_TOKEN; + const org = process.env.GITHUB_ORG || 'vega'; + + if (!token) { + warn('GITHUB_TOKEN is not set; skipping GitHub org drift checks.'); + process.exit(hasErrors ? 1 : 0); + } + + const orgRepos = await fetchPublicRepos(org, token); + const orgRepoNames = new Set(orgRepos.map((repo) => repo.name)); + + for (const repoName of orgRepoNames) { + if (!inventoryByName.has(repoName) && !uncategorized.has(repoName)) { + fail(`Public repository '${repoName}' is missing from project-inventory.yml.`); + hasErrors = true; + } + } + + for (const [repoName, entry] of inventoryByName.entries()) { + if (!orgRepoNames.has(repoName)) { + fail(`Inventory entry '${repoName}' does not match an existing public repository in ${org}.`); + hasErrors = true; + continue; + } + + const repo = orgRepos.find((r) => r.name === repoName); + if (repo && repo.archived && entry.lifecycle_status !== 'archived') { + const hasExplicitReason = typeof entry.notes === 'string' && entry.notes.trim().length > 0; + if (!hasExplicitReason) { + fail(`'${repoName}' is archived on GitHub but lifecycle_status is '${entry.lifecycle_status}' with no explicit note.`); + hasErrors = true; + } else { + warn(`'${repoName}' is archived on GitHub but inventory lifecycle_status is '${entry.lifecycle_status}'. Notes provided; confirm intent.`); + } + } + } + + if (hasErrors) { + process.exit(1); + } + + console.log('Project inventory checks passed.'); +} + +main().catch((err) => { + fail(err.stack || String(err)); + process.exit(1); +}); From b7190972d01d79ca8cb6d9e690661a7b535bc6fd Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sun, 24 May 2026 23:28:32 -0400 Subject: [PATCH 2/7] Add deduped issue alerts for scheduled inventory check failures --- .../project-inventory-ci-alert.md | 10 +++++ .github/workflows/check-project-inventory.yml | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/project-inventory-ci-alert.md diff --git a/.github/ISSUE_TEMPLATE/project-inventory-ci-alert.md b/.github/ISSUE_TEMPLATE/project-inventory-ci-alert.md new file mode 100644 index 0000000..cd800f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/project-inventory-ci-alert.md @@ -0,0 +1,10 @@ +## Project inventory scheduled check failed + +The scheduled `check-project-inventory` workflow failed. + +- Workflow: `check-project-inventory.yml` +- Trigger: `schedule` +- Repository: `${{ github.repository }}` +- Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + +Please investigate the failure and push a fix. This issue is auto-created once and auto-closed when the scheduled check succeeds again. diff --git a/.github/workflows/check-project-inventory.yml b/.github/workflows/check-project-inventory.yml index c976e8f..33ec4ed 100644 --- a/.github/workflows/check-project-inventory.yml +++ b/.github/workflows/check-project-inventory.yml @@ -16,6 +16,7 @@ on: permissions: contents: read + issues: write jobs: check: @@ -30,3 +31,40 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_ORG: vega + + - name: Create failure alert issue (scheduled runs) + if: failure() && github.event_name == 'schedule' + uses: peter-evans/create-issue-from-file@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + title: "CI alert: project-inventory scheduled check failing" + labels: ci-alert,project-inventory + content-filepath: .github/ISSUE_TEMPLATE/project-inventory-ci-alert.md + search-existing: open + + - name: Close failure alert issue on recovery (scheduled runs) + if: success() && github.event_name == 'schedule' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const title = "CI alert: project-inventory scheduled check failing"; + const { data: issues } = await github.rest.issues.listForRepo({ + owner, + repo, + state: "open", + labels: "ci-alert,project-inventory", + per_page: 100 + }); + for (const issue of issues) { + if (issue.title === title) { + await github.rest.issues.update({ + owner, + repo, + issue_number: issue.number, + state: "closed" + }); + } + } From 80f191174761dd26c277a46bc5ec3430c24fc741 Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sun, 24 May 2026 23:32:54 -0400 Subject: [PATCH 3/7] Add archived_repos inventory group and move Voyager projects --- PROJECT_STATUS.md | 1 + project-inventory.schema.yml | 64 ++++++++++++++++++++++++++++++++++++ project-inventory.yml | 6 ++++ 3 files changed, 71 insertions(+) diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index 92a8e68..2f3ffde 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -10,6 +10,7 @@ Field usage: - `uncategorized_repos` is a holding list for known public repos that are intentionally not fully classified yet (often lightly maintained, historical, or low-signal repos). - `core_maintainers` is optional and should be used when a repository has clear, consistent maintainer leadership. - `research_prototypes` groups research repositories that share common lifecycle/support/ownership defaults to reduce repetitive metadata. +- `archived_repos` groups archived repositories separately from active or maintenance groups so historical projects are explicit. ## `lifecycle_status` diff --git a/project-inventory.schema.yml b/project-inventory.schema.yml index 40da304..4da795a 100644 --- a/project-inventory.schema.yml +++ b/project-inventory.schema.yml @@ -148,3 +148,67 @@ properties: type: array items: type: string + # Archived repos grouped with shared defaults to keep historical projects separate. + archived_repos: + type: object + required: + - defaults + - repos + additionalProperties: false + properties: + defaults: + type: object + additionalProperties: false + required: + - lifecycle_status + - project_role + - owner_model + properties: + lifecycle_status: + enum: + - archived + support_level: + enum: + - full + - best_effort + - none + project_role: + enum: + - historical + owner_model: + enum: + - org_shared + - named_lead + - unowned + repos: + type: array + items: + oneOf: + - type: string + - type: object + additionalProperties: false + required: + - name + properties: + name: + type: string + lifecycle_status: + enum: + - archived + support_level: + enum: + - full + - best_effort + - none + project_role: + enum: + - historical + owner_model: + enum: + - org_shared + - named_lead + - unowned + core_maintainers: + type: array + items: + type: string diff --git a/project-inventory.yml b/project-inventory.yml index ac4162b..1aab9b8 100644 --- a/project-inventory.yml +++ b/project-inventory.yml @@ -217,6 +217,12 @@ research_prototypes: - compassql - falcon - lyra +archived_repos: + defaults: + lifecycle_status: archived + project_role: historical + owner_model: unowned + repos: - voyager - voyager-docs - voyager-electron From 70de2150df4c2d34cde61c6ace679fd764d4ae7a Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sun, 24 May 2026 23:34:36 -0400 Subject: [PATCH 4/7] Simplify archived projects to flat inventory list --- PROJECT_STATUS.md | 2 +- project-inventory.schema.yml | 68 +++--------------------------- project-inventory.yml | 15 +++---- scripts/check-project-inventory.js | 10 ++++- 4 files changed, 20 insertions(+), 75 deletions(-) diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index 2f3ffde..7278ea6 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -10,7 +10,7 @@ Field usage: - `uncategorized_repos` is a holding list for known public repos that are intentionally not fully classified yet (often lightly maintained, historical, or low-signal repos). - `core_maintainers` is optional and should be used when a repository has clear, consistent maintainer leadership. - `research_prototypes` groups research repositories that share common lifecycle/support/ownership defaults to reduce repetitive metadata. -- `archived_repos` groups archived repositories separately from active or maintenance groups so historical projects are explicit. +- `archived_repos` is a flat list of historical repositories; entries implicitly map to `lifecycle_status: archived`, `project_role: historical`, `owner_model: unowned`, and `support_level: none`. ## `lifecycle_status` diff --git a/project-inventory.schema.yml b/project-inventory.schema.yml index 4da795a..eece12c 100644 --- a/project-inventory.schema.yml +++ b/project-inventory.schema.yml @@ -148,67 +148,9 @@ properties: type: array items: type: string - # Archived repos grouped with shared defaults to keep historical projects separate. + # Archived repos as a flat list. Archived implies lifecycle_status:archived, + # project_role:historical, owner_model:unowned, support_level:none. archived_repos: - type: object - required: - - defaults - - repos - additionalProperties: false - properties: - defaults: - type: object - additionalProperties: false - required: - - lifecycle_status - - project_role - - owner_model - properties: - lifecycle_status: - enum: - - archived - support_level: - enum: - - full - - best_effort - - none - project_role: - enum: - - historical - owner_model: - enum: - - org_shared - - named_lead - - unowned - repos: - type: array - items: - oneOf: - - type: string - - type: object - additionalProperties: false - required: - - name - properties: - name: - type: string - lifecycle_status: - enum: - - archived - support_level: - enum: - - full - - best_effort - - none - project_role: - enum: - - historical - owner_model: - enum: - - org_shared - - named_lead - - unowned - core_maintainers: - type: array - items: - type: string + type: array + items: + type: string diff --git a/project-inventory.yml b/project-inventory.yml index 1aab9b8..8b806e9 100644 --- a/project-inventory.yml +++ b/project-inventory.yml @@ -218,13 +218,8 @@ research_prototypes: - falcon - lyra archived_repos: - defaults: - lifecycle_status: archived - project_role: historical - owner_model: unowned - repos: - - voyager - - voyager-docs - - voyager-electron - - voyager-server - - voyager2 + - voyager + - voyager-docs + - voyager-electron + - voyager-server + - voyager2 diff --git a/scripts/check-project-inventory.js b/scripts/check-project-inventory.js index 0da25c9..74bc45a 100644 --- a/scripts/check-project-inventory.js +++ b/scripts/check-project-inventory.js @@ -77,7 +77,15 @@ async function main() { } return { ...(researchDefaults || {}), ...entry }; }); - const allEntries = [...repoEntries, ...researchEntries]; + const archivedEntriesRaw = inventory.archived_repos || []; + const archivedEntries = archivedEntriesRaw.map((name) => ({ + name, + lifecycle_status: 'archived', + project_role: 'historical', + owner_model: 'unowned', + support_level: 'none', + })); + const allEntries = [...repoEntries, ...researchEntries, ...archivedEntries]; const uncategorized = new Set(inventory.uncategorized_repos || []); const inventoryByName = new Map(); let hasErrors = false; From 713e2fd8010dab523e4ec7aff5417ae515a06774 Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sun, 24 May 2026 23:40:12 -0400 Subject: [PATCH 5/7] Move archived repos out of uncategorized and relax replacement warnings --- project-inventory.yml | 81 +++++++++++++++--------------- scripts/check-project-inventory.js | 5 +- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/project-inventory.yml b/project-inventory.yml index 8b806e9..891821a 100644 --- a/project-inventory.yml +++ b/project-inventory.yml @@ -131,7 +131,6 @@ repos: uncategorized_repos: - altair_ally - altair_tiles - - compass - compassql-analysis - dataflow-api - dataflow-vis @@ -140,65 +139,26 @@ uncategorized_repos: - ipyvega - logos - polestar - - react-vega-lite - scalable-vega - SciPy2024-Altair-Tutorial - ts-api - vega-benchmarks - vega-bundler - - vega-canvas - - vega-crossfilter - - vega-dataflow - vega-dataflow-examples - - vega-desktop - - vega-editor - - vega-embed-v2 - - vega-encode - - vega-event-selector - - vega-expression - - vega-force - - vega-geo - vega-gist - - vega-hierarchy - - vega-label - - vega-lib - - vega-lite-dev-config - vega-lite-params-proposal - vega-lite-pr-previews - vega-lite-shorthand - vega-lite-to-api - vega-lite-transforms2sql - vega-lite-tutorials - - vega-lite-ui - - vega-lite-v1 - - vega-lite-v2 - - vega-lite-v3 - - vega-lite-v4 - - vega-loader - vega-logging - - vega-observable - - vega-parser - vega-plus - - vega-projection - - vega-projection-extended - - vega-render-service - vega-renderer-webgl - - vega-runtime - - vega-scale - - vega-scenegraph - - vega-statistics - vega-tile - - vega-transforms - vega-tutorials - - vega-typings - - vega-util - - vega-view - - vega-view-transforms - - vega-voronoi - - vega-vscode - vega-webgl-renderer - vega-webgpu - - vega-wordcloud - vega2-docs - vegafusion-conda-feedstock - vegafusion-conda-jupyter-feedstock @@ -223,3 +183,44 @@ archived_repos: - voyager-electron - voyager-server - voyager2 + - compass + - react-vega-lite + - vega-canvas + - vega-crossfilter + - vega-dataflow + - vega-desktop + - vega-editor + - vega-embed-v2 + - vega-encode + - vega-event-selector + - vega-expression + - vega-force + - vega-geo + - vega-hierarchy + - vega-label + - vega-lib + - vega-lite-dev-config + - vega-lite-ui + - vega-lite-v1 + - vega-lite-v2 + - vega-lite-v3 + - vega-lite-v4 + - vega-loader + - vega-observable + - vega-parser + - vega-projection + - vega-projection-extended + - vega-render-service + - vega-runtime + - vega-scale + - vega-scenegraph + - vega-statistics + - vega-transforms + - vega-typings + - vega-util + - vega-view + - vega-view-transforms + - vega-voronoi + - vega-vscode + - vega-wordcloud + - web-service diff --git a/scripts/check-project-inventory.js b/scripts/check-project-inventory.js index 74bc45a..a00a0e4 100644 --- a/scripts/check-project-inventory.js +++ b/scripts/check-project-inventory.js @@ -118,10 +118,7 @@ async function main() { } } - if ( - (entry.lifecycle_status === 'deprecated' || entry.lifecycle_status === 'archived') && - !entry.replacement - ) { + if (entry.lifecycle_status === 'deprecated' && !entry.replacement) { warn(`'${entry.name}' is ${entry.lifecycle_status} and has no replacement set. Verify this is intentional.`); } } From f53182872d833cc65409c0f5fce07e3681e819ee Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Tue, 26 May 2026 13:21:22 -0400 Subject: [PATCH 6/7] Trim initial lead cohort: editor, vl-convert, vl-convert-service - Remove hydrosquall as secondary (core_maintainer) for editor - Keep jonmmease as sole lead only for vegafusion - Remove jonmmease as named lead from vl-convert and vl-convert-service, switch to org_shared - Set vl-convert lifecycle_status to maintenance --- project-inventory.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/project-inventory.yml b/project-inventory.yml index 891821a..80c764c 100644 --- a/project-inventory.yml +++ b/project-inventory.yml @@ -23,7 +23,6 @@ repos: owner_model: named_lead core_maintainers: - domoritz - - hydrosquall repo_lead: domoritz - name: editor-backend lifecycle_status: maintenance @@ -111,19 +110,13 @@ repos: - jonmmease repo_lead: jonmmease - name: vl-convert - lifecycle_status: active + lifecycle_status: maintenance project_role: ecosystem - owner_model: named_lead - core_maintainers: - - jonmmease - repo_lead: jonmmease + owner_model: org_shared - name: vl-convert-service lifecycle_status: maintenance project_role: integration - owner_model: named_lead - core_maintainers: - - jonmmease - repo_lead: jonmmease + owner_model: org_shared - name: vue-vega lifecycle_status: maintenance project_role: integration From a685961ef6b4cb25ea337e115784a7b2e0957400 Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Tue, 26 May 2026 18:06:03 -0400 Subject: [PATCH 7/7] docs: tighten wording in PROJECT_STATUS.md --- PROJECT_STATUS.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index 7278ea6..9b8bf4b 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -5,12 +5,12 @@ This document defines public metadata values used by [`project-inventory.yml`](. Field usage: - `support_level` is optional; default inference is `active -> full`, `experimental -> best_effort`, `maintenance -> best_effort`, `deprecated -> none`, `archived -> none`. -- `replacement` is optional and should be included when a clear migration target exists. -- `notes` is optional and should be used sparingly for exceptions that are not captured by enum fields. -- `uncategorized_repos` is a holding list for known public repos that are intentionally not fully classified yet (often lightly maintained, historical, or low-signal repos). -- `core_maintainers` is optional and should be used when a repository has clear, consistent maintainer leadership. -- `research_prototypes` groups research repositories that share common lifecycle/support/ownership defaults to reduce repetitive metadata. -- `archived_repos` is a flat list of historical repositories; entries implicitly map to `lifecycle_status: archived`, `project_role: historical`, `owner_model: unowned`, and `support_level: none`. +- `replacement` is optional; include when a clear migration target exists. +- `notes` is optional; use sparingly for exceptions not captured by enum fields. +- `uncategorized_repos` holds known public repos not yet fully classified (often lightly maintained, historical, or low-signal). +- `core_maintainers` is optional; use when a repository has clear, consistent maintainer leadership. +- `research_prototypes` groups research repositories sharing common lifecycle/support/ownership defaults. +- `archived_repos` is a flat list of historical repositories; entries map to `lifecycle_status: archived`, `project_role: historical`, `owner_model: unowned`, and `support_level: none`. ## `lifecycle_status` @@ -19,16 +19,16 @@ Field usage: | `active` | Normal development. Issues, pull requests, and releases are expected. | | `experimental` | Exploratory work. APIs, scope, or long-term maintenance may change. | | `maintenance` | Kept working, but limited new feature development. | -| `deprecated` | Users should migrate elsewhere. A replacement should be listed where possible. | -| `archived` | Historical or read-only project. No support is expected. | +| `deprecated` | Users should migrate elsewhere; list a replacement where possible. | +| `archived` | Historical or read-only. No support expected. | ## `support_level` | Value | Meaning | | --- | --- | -| `full` | Maintainers expect to review issues/security reports and publish releases as appropriate. | +| `full` | Maintainers review issues/security reports and publish releases as appropriate. | | `best_effort` | Maintained when time allows; no strong response commitment. | -| `none` | Historical or unsupported; no maintainer response should be expected. | +| `none` | Unsupported; no maintainer response expected. | ## `project_role` @@ -48,4 +48,4 @@ Field usage: | --- | --- | | `org_shared` | The Vega maintainer group collectively owns the repository. | | `named_lead` | We have a primary maintainer for triage, release, or review leadership | -| `unowned` | No active maintainer ownership; generally reserved for archived or unsupported repositories. | +| `unowned` | No active maintainer; reserved for archived or unsupported repositories. |