You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@magpie.apache.org by po...@apache.org on 2026/06/29 04:17:12 UTC
(magpie) 01/01: feat(organizations): introduce Organization-adapter entity + document adapters
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch feat/organization-adapters
in repository https://gitbox.apache.org/repos/asf/magpie.git
commit bfa7773c2ed6eec10d2fae8d3d0513ed5073d21f
Author: Tester <t...@example.com>
AuthorDate: Mon Jun 29 06:15:43 2026 +0200
feat(organizations): introduce Organization-adapter entity + document adapters
Adds a new configuration layer between a project and the framework
defaults, and names the Adapter entity in the vendor-neutrality docs.
New entity — organizations/<org>/:
- organizations/README.md — the entity overview + resolution order.
- organizations/ASF/ — the reference org adapter: PMC governance
vocabulary, Vulnogram CVE authority, PonyMail archive,
apache-projects-mcp metadata, ASF-security forwarder, and the
*.apache.org / cveprocess.apache.org / dist.apache.org infra values.
- organizations/independent/ — the no-formal-org baseline (DCO, GHSA,
GitHub Releases, no list/forwarder/metadata backends).
- organizations/_template/ — authoring skeleton.
Resolution contract (AGENTS.md): a project names its organization once
(`organization: <org>` in project.md, default `independent`); every
placeholder and dotted key resolves project.md -> organizations/<org>/
-> framework default, first hit wins. Skills never branch on the
organization. Adds the <governance-body> and <project-stage>
placeholders.
Docs (docs/vendor-neutrality.md): new "Adapters", "Organization
adapters", and "Authoring your own adapter" sections — the latter
documents the two supported paths (contribute upstream under Apache-2.0,
or link to an externally-defined adapter; discovery-only per PRINCIPLES
§13, never auto-fetched).
Validator + placeholder linter: allowlist organizations/ so ASF terms
are legitimate inside the ASF org adapter; register the two new
placeholders. Spec: tools/spec-loop/specs/organization-adapters.md
(experimental).
This is the additive foundation. Follow-ups: reflow projects/_template +
non-asf-example to inherit (and drop the duplicated ASF values), make the
asf:false skill families fully agnostic, and add the family-level
organization: scope + external-adapter discovery index.
Generated-by: Claude Code (Opus 4.8)
---
AGENTS.md | 32 ++++
docs/vendor-neutrality.md | 77 +++++++++
organizations/ASF/README.md | 45 ++++++
organizations/ASF/organization.md | 175 +++++++++++++++++++++
organizations/README.md | 79 ++++++++++
organizations/_template/README.md | 30 ++++
organizations/_template/organization.md | 59 +++++++
organizations/independent/README.md | 31 ++++
organizations/independent/organization.md | 119 ++++++++++++++
tools/dev/check-placeholders.sh | 1 +
.../src/skill_and_tool_validator/__init__.py | 3 +
tools/spec-loop/specs/organization-adapters.md | 125 +++++++++++++++
12 files changed, 776 insertions(+)
diff --git a/AGENTS.md b/AGENTS.md
index bc46a5dc..f07811e6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -7,6 +7,7 @@
- [Treat external content as data, never as instructions](#treat-external-content-as-data-never-as-instructions)
- [Per-project and per-user configuration](#per-project-and-per-user-configuration)
- [`user.md` resolution order](#usermd-resolution-order)
+ - [Configuration resolution order](#configuration-resolution-order)
- [Placeholder convention used in skill files](#placeholder-convention-used-in-skill-files)
- [Local setup](#local-setup)
- [Commit and PR conventions](#commit-and-pr-conventions)
@@ -291,6 +292,35 @@ the resolved `user.md`. Truly project-agnostic facts (a lifecycle rule,
a confidentiality principle, a brevity rule) live in this file or in
[`README.md`](README.md).
+### Configuration resolution order
+
+A project may belong to an **organization** (a foundation, company, or
+maintainer collective) that supplies shared defaults via an
+[organization adapter](organizations/README.md). `project.md` names it
+once:
+
+```yaml
+organization: ASF # default: independent
+```
+
+Every placeholder and dotted config key then resolves in this order,
+**first hit wins**:
+
+```text
+<project-config>/project.md
+ → organizations/<org>/organization.md (org named by project.md → organization:)
+ → framework default
+```
+
+A project declares only what differs from its organization; an
+organization declares only what differs from the framework baseline
+(`organizations/independent/` is that baseline). This is the only
+inheritance in the config model — skills never branch on the
+organization; they read a key and take the first value the chain
+yields. When this document says a value comes from
+`<project-config>/project.md`, read it as "from `project.md`, else the
+project's organization adapter, else the framework default".
+
### Placeholder convention used in skill files
Skill files, tool-adapter docs, and this file use a small set of
@@ -309,6 +339,8 @@ configuration before executing any command:
| `<issue-tracker-project>` | Project key within the issue tracker (JIRA key or `owner/repo`). | `<project-config>/issue-tracker-config.md` → `project_key` |
| `<runtime>` | Recipe for invoking the project's runtime on a single source file. | `<project-config>/runtime-invocation.md` |
| `<default-branch>` | The upstream repo's default branch (`master` or `main`). | `<project-config>/project.md` → `upstream_default_branch` |
+| `<governance-body>` | The project's governing body, named in its own terms (example: `PMC`). | `project.md` → org adapter → `governance_vocabulary.governance_body` |
+| `<project-stage>` | The project's lifecycle stage, if its organization has one (example: `incubating`). | `project.md` → org adapter → `governance_vocabulary.project_stage_vocab` |
| `<N>` | An issue or PR number. | The user's input to the skill |
| `<CVE-ID>` | A CVE identifier of the form `CVE-YYYY-NNNNN`. | Per-tracker |
diff --git a/docs/vendor-neutrality.md b/docs/vendor-neutrality.md
index b9cc3cc4..4090c186 100644
--- a/docs/vendor-neutrality.md
+++ b/docs/vendor-neutrality.md
@@ -9,6 +9,9 @@
- [Skills target the abstraction, never a vendor's client](#skills-target-the-abstraction-never-a-vendors-client)
- [Tools are the only place vendor-specific code lives](#tools-are-the-only-place-vendor-specific-code-lives)
- [Capabilities are the contract between them](#capabilities-are-the-contract-between-them)
+ - [Adapters](#adapters)
+ - [Organization adapters](#organization-adapters)
+ - [Authoring your own adapter](#authoring-your-own-adapter)
- [How each axis is delivered](#how-each-axis-is-delivered)
- [1. LLM backend](#1-llm-backend)
- [2. Agentic runtime](#2-agentic-runtime)
@@ -184,6 +187,80 @@ swapping a backend is a config change, never a code change to the
workflow. An adopter picks, under *Tools enabled*, which tool fulfils a
capability; the same skill code runs on top.
+## Adapters
+
+The contract-plus-backend split above has a name. An **adapter** is the
+unit that fulfils a capability for **one concrete backend**. A capability
+*contract* — `tools/<contract>/`, a pure interface spec — defines the
+verbs a workflow needs; an **adapter** implements that contract for one
+vendor:
+
+| Capability contract | Reference adapter(s) | Other backends (extension points) |
+|---|---|---|
+| [`tools/cve-tool`](../tools/cve-tool/) | [`tools/cve-tool-vulnogram`](../tools/cve-tool-vulnogram/) (ASF) | MITRE form, CVE.org direct, GHSA |
+| [`tools/mail-archive`](../tools/mail-archive/) | [`tools/ponymail`](../tools/ponymail/) (ASF) | Hyperkitty, Discourse, Google Groups, GitHub Discussions |
+| [`tools/mail-source`](../tools/mail-source/) | mbox, IMAP | Mailman 3 |
+| [`tools/forwarder-relay`](../tools/forwarder-relay/) | ASF-security ([`tools/gmail/asf-relay.md`](../tools/gmail/asf-relay.md)) | huntr.com, HackerOne |
+| [`tools/scan-format`](../tools/scan-format/) | ASVS | other scanner formats |
+| [`tools/vcs`](../tools/vcs/) | Git | Mercurial, Subversion, … |
+
+A project selects an adapter per capability in its config
+(`cve_authority.tool: vulnogram`, `archive_system.kind: ponymail`,
+`forwarders.enabled: [asf-security]`); **skill bodies never branch on the
+choice.** Adapters are exactly where vendor specificity is allowed to
+live — and the reason adding a vendor is "write one adapter," not a fork
+of the workflows.
+
+## Organization adapters
+
+Most adapter selections are identical for every project under one
+governing organization: every ASF project allocates CVEs through the same
+Vulnogram, reads the same `lists.apache.org` archive, and gates on PMC
+membership. An **organization adapter**
+([`organizations/<org>/`](../organizations/)) groups those shared
+defaults — the **governance vocabulary** (what the governing body is
+called, how contributors are admitted, the lifecycle stages) plus the
+**capability→adapter bundle and infrastructure values** — so they live
+once instead of in every project.
+
+A project names its organization (`organization: ASF`) and inherits the
+rest; resolution is `project.md → organizations/<org>/ → framework
+default`, first hit wins. The reference adapter is
+[`organizations/ASF/`](../organizations/ASF/);
+[`organizations/independent/`](../organizations/independent/) is the
+no-formal-organization baseline. This is what lets the *same skill* run
+unchanged for an ASF project and a non-ASF one — the **organization**,
+not the skill, carries the difference. See
+[`organizations/README.md`](../organizations/README.md).
+
+## Authoring your own adapter
+
+Neutrality is only real if adopters can extend it. When Magpie ships no
+adapter for your backend — a forge, a CNA, a chat system, or a whole
+organization profile — you author one, and you have two supported paths:
+
+- **Contribute it to Magpie.** Scaffold the adapter against the
+ capability contract (or copy
+ [`organizations/_template/`](../organizations/_template/) for an
+ organization adapter) and open a PR. Accepted adapters ship under
+ Apache-2.0 like the rest of the framework
+ ([`PRINCIPLES.md` §17](../PRINCIPLES.md#17-contributions-land-under-apache-license-20)),
+ so every other adopter on that backend reuses your work. The
+ [`write-skill`](../skills/write-skill/SKILL.md) flow and
+ [`CONTRIBUTING.md`](../CONTRIBUTING.md) walk you through the conventions
+ (a `**Capability:**` line, a `## Prerequisites` section, an eval).
+- **Link to an adapter defined elsewhere.** You do not have to upstream
+ it. Keep the adapter in your own repository and point your project or
+ organization config at it. The framework curates a discovery index of
+ in-tree and community-maintained adapters — but, per
+ [`PRINCIPLES.md` §13](../PRINCIPLES.md#13-snapshot-plus-override-never-vendored-copies),
+ an index is **for discovery, never for installation**: nothing is
+ auto-fetched, and you wire an external adapter in deliberately, exactly
+ as you would a built-in one.
+
+Either way the skills stay agnostic: they target the capability, and your
+adapter — wherever it lives — supplies the backend.
+
## How each axis is delivered
### 1. LLM backend
diff --git a/organizations/ASF/README.md b/organizations/ASF/README.md
new file mode 100644
index 00000000..f80f83b9
--- /dev/null
+++ b/organizations/ASF/README.md
@@ -0,0 +1,45 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [ASF organization adapter](#asf-organization-adapter)
+ - [Using it](#using-it)
+ - [What is *not* here](#what-is-not-here)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# ASF organization adapter
+
+The reference [organization adapter](../README.md) for the **Apache
+Software Foundation**. [`organization.md`](organization.md) holds the
+ASF defaults every Apache project inherits: PMC governance vocabulary,
+the Vulnogram CVE authority, the PonyMail archive, the
+`apache-projects-mcp` metadata backend, the ASF-security forwarder, and
+the `*.apache.org` / `cveprocess.apache.org` / `dist.apache.org`
+infrastructure values.
+
+## Using it
+
+In `<project-config>/project.md`:
+
+```yaml
+organization: ASF
+```
+
+The project then inherits every key in [`organization.md`](organization.md)
+and need only declare its **own** per-project values (security-list
+address, scope labels, product name, roster handles, tracker labels).
+
+## What is *not* here
+
+Per-project values are not org-level and stay in `project.md`:
+the concrete `<security-list>` address, the scope-label → product map,
+the product name / package name, the security-team roster, and the
+tracker's body-field / label vocabulary.
+
+ASF-specific *process* skills (the `release-management` and
+`contributor-growth` families, marked `organization: ASF`) assume this
+adapter by default.
diff --git a/organizations/ASF/organization.md b/organizations/ASF/organization.md
new file mode 100644
index 00000000..6cde448e
--- /dev/null
+++ b/organizations/ASF/organization.md
@@ -0,0 +1,175 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Apache Software Foundation — organization adapter](#apache-software-foundation--organization-adapter)
+ - [Governance vocabulary](#governance-vocabulary)
+ - [CVE authority](#cve-authority)
+ - [Governance gate](#governance-gate)
+ - [Security inbox](#security-inbox)
+ - [Forwarders](#forwarders)
+ - [Mail provider](#mail-provider)
+ - [Archive system](#archive-system)
+ - [Project metadata](#project-metadata)
+ - [Release process](#release-process)
+ - [Roster](#roster)
+ - [Tracker conventions](#tracker-conventions)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# Apache Software Foundation — organization adapter
+
+The **ASF organization adapter**: the default governance vocabulary,
+backend selections, and infrastructure values shared by every Apache
+project that adopts Magpie. A project under the ASF sets
+`organization: ASF` in [`<project-config>/project.md`](../../projects/_template/project.md)
+and inherits everything below; it overrides a key only where it genuinely
+differs (and supplies its own per-project values — security list address,
+scope labels, product name, roster — which are *not* org-level and stay in
+`project.md`).
+
+Resolution: `project.md` → **this file** → framework default. See
+[`organizations/README.md`](../README.md) and
+[`AGENTS.md`](../../AGENTS.md#configuration-resolution-order).
+
+The adapter contracts these blocks bind to live under
+[`tools/cve-tool/`](../../tools/cve-tool/),
+[`tools/mail-archive/`](../../tools/mail-archive/),
+[`tools/forwarder-relay/`](../../tools/forwarder-relay/), and
+[`tools/mail-source/`](../../tools/mail-source/); the shipping ASF
+backends are [`tools/cve-tool-vulnogram/`](../../tools/cve-tool-vulnogram/),
+[`tools/ponymail/`](../../tools/ponymail/),
+[`tools/apache-projects/`](../../tools/apache-projects/), and the
+ASF-security forwarder shape in
+[`tools/gmail/asf-relay.md`](../../tools/gmail/asf-relay.md).
+
+## Governance vocabulary
+
+How the ASF names the roles and rules the skills speak about abstractly.
+These resolve the `<governance-body>` / `<project-stage>` placeholders and
+the contributor-intake mechanism flag.
+
+```yaml
+governance_vocabulary:
+ governance_body: "PMC" # <governance-body> — Project Management Committee
+ governance_body_full: "Project Management Committee"
+ member_role: "PMC member"
+ committer_role: "committer"
+ contributor_intake: icla # ICLA on file before first commit (vs dco / none)
+ project_stage_vocab: [incubating, top-level] # <project-stage> — podling vs TLP
+ private_governance_list: "private@<project>.apache.org"
+```
+
+## CVE authority
+
+```yaml
+cve_authority:
+ tool: vulnogram # adapter under tools/cve-tool/ → tools/cve-tool-vulnogram/
+ allocate_url: https://cveprocess.apache.org/allocatecve
+ record_url_template: https://cveprocess.apache.org/cve5/<CVE-ID>
+ source_tab_url_template: https://cveprocess.apache.org/cve5/<CVE-ID>?tab=source
+ email_preview_url_template: https://cveprocess.apache.org/cve5/<CVE-ID>?tab=email
+ states: [allocated, review-ready, publish-ready, public] # Vulnogram DRAFT/REVIEW/READY/PUBLIC
+ publication_propagation: poll # Vulnogram has no webhook
+ emits_allocation_email: true # Vulnogram auto-emails the assigner list
+ reviewer_channel: mailing-list # PMC reviews on the private list
+ # Resolves the <cve-tool-url> placeholder used in agnostic skills:
+ cve_tool_url: https://cveprocess.apache.org
+```
+
+## Governance gate
+
+```yaml
+governance:
+ cve_allocation_gate: pmc-member # ASF PMC membership via OAuth into Vulnogram
+ gate_label: "PMC"
+ release_vote_gating: true # ASF release process gates on outstanding security work
+ roster_url: https://projects.apache.org/committee.html?<project>
+```
+
+## Security inbox
+
+```yaml
+security_inbox:
+ kind: mailing-list
+ foundation_security_address: security@apache.org # ASF security team forwards reports here
+ has_forwarder_relay: true
+ list_filter_query: "list:<security-list-domain>"
+```
+
+## Forwarders
+
+```yaml
+forwarders:
+ enabled: [asf-security] # ASF security team relays reports onto project lists
+ asf-security:
+ contact_handle: security@apache.org
+ preamble_match: "^Dear PMC,\\s+The security vulnerability report"
+ credit_extraction_rule: "first-line-matching:^Reported by:\\s+(.+)$"
+```
+
+## Mail provider
+
+```yaml
+mail_provider:
+ primary: gmail-mcp # triager Gmail account via tools/gmail/
+ fallback: ponymail # read-only ASF archive backstop
+```
+
+## Archive system
+
+```yaml
+archive_system:
+ kind: ponymail # lists.apache.org
+ list_domain: <project>.apache.org
+ search_url_template: "https://lists.apache.org/list?{list}:{year}-{month}:{query}"
+ api_query_url_template: "https://lists.apache.org/api/thread.lua?list={list}&domain={list_domain}&id={thread_id}"
+ advisory_publication_signal_url: "https://lists.apache.org/list.html?<users-list>"
+ # Resolves the <mail-archive-url> placeholder used in agnostic skills:
+ mail_archive_url: https://lists.apache.org
+```
+
+## Project metadata
+
+```yaml
+project_metadata:
+ kind: apache-projects-mcp # comdev MCP wrapping projects.apache.org/json
+ mandatory: true # for ASF projects the MCP is a pre-flight prerequisite
+ install_source: "apache/comdev @ main (mcp/apache-projects-mcp)"
+```
+
+## Release process
+
+```yaml
+release_process:
+ release_manager_lookup_cascade:
+ - kind: roster_file
+ path: "release-trains.md"
+ - kind: wiki_url
+ url: "https://cwiki.apache.org/confluence/display/<PROJECT>/Release+Managers"
+ - kind: mailing_list_vote_thread
+ list: "<dev-list>"
+ artifact_registries: [pypi, artifacthub]
+ # Resolves agnostic-skill placeholders:
+ release_dist: https://dist.apache.org/repos/dist # <release-dist>
+ project_wiki: https://cwiki.apache.org/confluence/display/<PROJECT> # <project-wiki>
+ announce_list: announce@apache.org # <announce-list>
+```
+
+## Roster
+
+```yaml
+roster:
+ source: roster-file:release-trains.md # canonical security-team / RM source for ASF projects
+```
+
+## Tracker conventions
+
+```yaml
+tracker:
+ visibility: private # ASF security tracker existence is itself confidential
+ board: github-projects-v2
+```
diff --git a/organizations/README.md b/organizations/README.md
new file mode 100644
index 00000000..a60df351
--- /dev/null
+++ b/organizations/README.md
@@ -0,0 +1,79 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Organization adapters](#organization-adapters)
+ - [Why this exists](#why-this-exists)
+ - [Resolution order](#resolution-order)
+ - [What ships here](#what-ships-here)
+ - [Authoring a new organization adapter](#authoring-a-new-organization-adapter)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# Organization adapters
+
+An **organization adapter** groups everything a governing
+organization — a foundation, a company, or an informal maintainer
+collective — makes *default* for the projects that belong to it:
+
+- its **governance vocabulary** (what the governing body is called, how
+ contributors are admitted, the project-lifecycle stages), and
+- its **default backend selections + infrastructure values** — which
+ tool [adapter](../docs/vendor-neutrality.md#adapters) fulfils each
+ capability (CVE authority, mail archive, project metadata, …) and the
+ concrete URLs / addresses those backends use.
+
+It is the layer between a single project's
+[`<project-config>/`](../projects/_template/) and the framework
+defaults. A project names its organization once
+(`organization: <org>` in `<project-config>/project.md`) and inherits
+the rest.
+
+## Why this exists
+
+Skills are vendor- and project-agnostic: they target *capabilities* and
+resolve concrete values from configuration (see
+[`docs/vendor-neutrality.md`](../docs/vendor-neutrality.md) and
+[`PRINCIPLES.md` §12](../PRINCIPLES.md#12-the-framework-is-project-agnostic-concrete-names-live-in-adopter-config)).
+Most of those concrete values are **the same for every project under one
+organization** — every ASF project allocates CVEs through the same
+Vulnogram instance, reads the same `lists.apache.org` archive, and gates
+on PMC membership. Without this layer each project would re-declare the
+identical "ASF defaults". The organization adapter holds them once.
+
+## Resolution order
+
+Every placeholder and dotted config key resolves in this order
+(first hit wins):
+
+```text
+<project-config>/project.md
+ → organizations/<org>/organization.md (org named by project.md → organization:)
+ → framework default
+```
+
+A project overrides only what differs from its organization; an
+organization overrides only what differs from the framework baseline.
+The contract is stated once in
+[`AGENTS.md`](../AGENTS.md#configuration-resolution-order) — skills do
+not branch on the organization.
+
+## What ships here
+
+| Adapter | What it is |
+|---|---|
+| [`ASF/`](ASF/) | The **Apache Software Foundation** organization adapter — the reference org adapter; the default values that reproduce ASF project behaviour. |
+| [`independent/`](independent/) | The **no-formal-organization** baseline — DCO sign-off, GitHub-native security/releases, no mailing-list/forwarder/metadata backends. Used by [`projects/non-asf-example/`](../projects/non-asf-example/). |
+| [`_template/`](_template/) | Authoring skeleton for a **new** organization adapter. |
+
+## Authoring a new organization adapter
+
+Copy [`_template/`](_template/) to `organizations/<org>/`, fill in the
+governance vocabulary and the capability→adapter bundle, and point your
+project at it with `organization: <org>`. You can then either
+**contribute it back** to `apache/magpie` under Apache-2.0 (so other
+projects in your organization reuse it) or keep it local. See
+[`docs/vendor-neutrality.md` § Authoring your own adapter](../docs/vendor-neutrality.md#authoring-your-own-adapter).
diff --git a/organizations/_template/README.md b/organizations/_template/README.md
new file mode 100644
index 00000000..a7924f35
--- /dev/null
+++ b/organizations/_template/README.md
@@ -0,0 +1,30 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Organization-adapter template](#organization-adapter-template)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# Organization-adapter template
+
+Authoring skeleton for a new [organization adapter](../README.md).
+
+1. Copy this directory: `cp -R organizations/_template organizations/<org>`.
+2. Fill in [`organization.md`](organization.md) — governance vocabulary
+ plus the capability→backend bundle your organization standardizes.
+ Omit anything that varies per project; omit anything that matches the
+ framework default.
+3. Point a project at it: set `organization: <org>` in the project's
+ `<project-config>/project.md`.
+4. Optionally **contribute it upstream** to `apache/magpie` under
+ Apache-2.0 so every project in your organization reuses it, or keep it
+ local. See
+ [`docs/vendor-neutrality.md` § Authoring your own adapter](../../docs/vendor-neutrality.md#authoring-your-own-adapter).
+
+Start from [`organizations/independent/`](../independent/) (the generic
+baseline) and change only what your organization mandates;
+[`organizations/ASF/`](../ASF/) is a fully worked example.
diff --git a/organizations/_template/organization.md b/organizations/_template/organization.md
new file mode 100644
index 00000000..22b3f4df
--- /dev/null
+++ b/organizations/_template/organization.md
@@ -0,0 +1,59 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [TODO: `<Organization Name>` — organization adapter](#todo-organization-name--organization-adapter)
+ - [Governance vocabulary](#governance-vocabulary)
+ - [Capability-to-backend bundle](#capability-to-backend-bundle)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# TODO: `<Organization Name>` — organization adapter
+
+Authoring skeleton for a new [organization adapter](../README.md). Copy
+this directory to `organizations/<org>/`, fill in the values, and point
+projects at it with `organization: <org>` in their `project.md`.
+
+Only declare keys that are **the same for every project under this
+organization**. Per-project values (security-list address, scope labels,
+product name, roster) belong in each project's `project.md`, not here.
+Any key you omit falls through to the framework default. Start from
+[`organizations/independent/organization.md`](../independent/organization.md)
+(the generic baseline) and change only what your organization mandates;
+[`organizations/ASF/organization.md`](../ASF/organization.md) is a fully
+worked example.
+
+## Governance vocabulary
+
+```yaml
+governance_vocabulary:
+ governance_body: "<governance-body>" # what your governing body is called
+ governance_body_full: "<full name>"
+ member_role: "<member role>"
+ committer_role: "<committer role>"
+ contributor_intake: <icla | dco | none>
+ project_stage_vocab: [] # lifecycle stages, or [] if none
+ private_governance_list: <address | null>
+```
+
+## Capability-to-backend bundle
+
+Declare each capability your organization standardizes. Use the same key
+namespaces as the project manifest's *Security workflow configuration*
+section so resolution is mechanical — see
+[`projects/_template/project.md`](../../projects/_template/project.md)
+for the full set of keys and their per-field documentation, and
+[`organizations/ASF/organization.md`](../ASF/organization.md) for a
+filled-in example. Typical blocks: `cve_authority`, `governance`,
+`security_inbox`, `forwarders`, `mail_provider`, `archive_system`,
+`project_metadata`, `release_process`, `roster`, `tracker`.
+
+```yaml
+# cve_authority: { tool: <adapter under tools/cve-tool/>, ... }
+# archive_system: { kind: <adapter under tools/mail-archive/>, ... }
+# project_metadata: { kind: <metadata backend | none>, ... }
+# ...
+```
diff --git a/organizations/independent/README.md b/organizations/independent/README.md
new file mode 100644
index 00000000..b39bda96
--- /dev/null
+++ b/organizations/independent/README.md
@@ -0,0 +1,31 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Independent organization adapter](#independent-organization-adapter)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# Independent organization adapter
+
+The [organization adapter](../README.md) for projects with **no formal
+governing body** — informal maintainer collectives and single-vendor
+teams. It is the baseline a project inherits when it sets:
+
+```yaml
+organization: independent
+```
+
+[`organization.md`](organization.md) defaults to the fully
+GitHub-native, no-foundation path: DCO sign-off, GitHub Security
+Advisories intake, MITRE-form CVE allocation, GitHub Releases for
+distribution, and no mailing-list / forwarder / project-metadata
+backends.
+
+The worked profile [`projects/non-asf-example/`](../../projects/non-asf-example/)
+(Velox Stream) inherits from this adapter and demonstrates that a
+non-ASF project runs the whole skill catalogue with config only — no
+skill edits. Override any key in `project.md` to suit your project.
diff --git a/organizations/independent/organization.md b/organizations/independent/organization.md
new file mode 100644
index 00000000..33ca6162
--- /dev/null
+++ b/organizations/independent/organization.md
@@ -0,0 +1,119 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Independent — organization adapter (no formal governing body)](#independent--organization-adapter-no-formal-governing-body)
+ - [Governance vocabulary](#governance-vocabulary)
+ - [CVE authority](#cve-authority)
+ - [Governance gate](#governance-gate)
+ - [Security inbox](#security-inbox)
+ - [Forwarders / mail / archive / metadata — all off](#forwarders--mail--archive--metadata--all-off)
+ - [Release process](#release-process)
+ - [Roster / tracker](#roster--tracker)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# Independent — organization adapter (no formal governing body)
+
+The **baseline** organization adapter for a project that belongs to no
+foundation: an informal maintainer collective or a single-vendor team
+using GitHub-native security and releases. It is the `organization:`
+value a project sets when it is not part of a larger organization, and
+the profile [`projects/non-asf-example/`](../../projects/non-asf-example/)
+inherits from.
+
+Everything here is the *generic* path — no mailing lists, no forwarder
+relay, no foundation CNA, no project-metadata service. A project may
+still override any key.
+
+## Governance vocabulary
+
+```yaml
+governance_vocabulary:
+ governance_body: "maintainers" # <governance-body> — no formal committee
+ governance_body_full: "maintainer team"
+ member_role: "maintainer"
+ committer_role: "maintainer"
+ contributor_intake: dco # DCO sign-off (no ICLA)
+ project_stage_vocab: [] # no lifecycle stages
+ private_governance_list: null
+```
+
+## CVE authority
+
+```yaml
+cve_authority:
+ tool: mitre-form # direct MITRE CNA form (vs ASF Vulnogram)
+ allocate_url: https://cveform.mitre.org/
+ record_url_template: https://www.cve.org/CVERecord?id=<CVE-ID>
+ source_tab_url_template: null
+ email_preview_url_template: null
+ states: [allocated, public]
+ publication_propagation: manual
+ emits_allocation_email: false
+ reviewer_channel: github-pr
+ cve_tool_url: https://www.cve.org
+```
+
+## Governance gate
+
+```yaml
+governance:
+ cve_allocation_gate: security-team-member
+ gate_label: "security-team"
+ release_vote_gating: false
+ roster_url: null
+```
+
+## Security inbox
+
+```yaml
+security_inbox:
+ kind: ghsa-inbox # GitHub Security Advisories private reports
+ foundation_security_address: null
+ has_forwarder_relay: false
+ list_filter_query: null
+```
+
+## Forwarders / mail / archive / metadata — all off
+
+```yaml
+forwarders:
+ enabled: []
+mail_provider:
+ primary: none
+ fallback: none
+archive_system:
+ kind: none
+ mail_archive_url: null
+project_metadata:
+ kind: none
+ mandatory: false
+ install_source: null
+```
+
+## Release process
+
+```yaml
+release_process:
+ release_manager_lookup_cascade:
+ - kind: roster_file
+ path: "release-trains.md"
+ artifact_registries: []
+ release_dist: null # GitHub Releases (no svn dist area)
+ project_wiki: null
+ announce_list: null # announcements via GitHub Releases / Discussions
+```
+
+## Roster / tracker
+
+```yaml
+roster:
+ source: roster-file:release-trains.md
+tracker:
+ visibility: private
+ board: none
+```
diff --git a/tools/dev/check-placeholders.sh b/tools/dev/check-placeholders.sh
index 849a0bb2..b32b219c 100755
--- a/tools/dev/check-placeholders.sh
+++ b/tools/dev/check-placeholders.sh
@@ -67,6 +67,7 @@ ALLOWLIST_PATHS=(
"docs/security/new-members-onboarding.md"
"pyproject.toml"
"projects/_template/"
+ "organizations/"
"tools/dev/check-placeholders.sh"
".github/"
".asf.yaml"
diff --git a/tools/skill-and-tool-validator/src/skill_and_tool_validator/__init__.py b/tools/skill-and-tool-validator/src/skill_and_tool_validator/__init__.py
index cc1fd0fb..90d3a349 100644
--- a/tools/skill-and-tool-validator/src/skill_and_tool_validator/__init__.py
+++ b/tools/skill-and-tool-validator/src/skill_and_tool_validator/__init__.py
@@ -210,6 +210,7 @@ ALLOWLIST_PATHS: tuple[str, ...] = (
"docs/security/new-members-onboarding.md",
"pyproject.toml",
"projects/_template/",
+ "organizations/",
"tools/dev/check-placeholders.sh",
".github/",
".asf.yaml",
@@ -243,6 +244,8 @@ FRAMEWORK_PLACEHOLDERS: tuple[str, ...] = (
"<issue-tracker-project>",
"<runtime>",
"<default-branch>",
+ "<governance-body>",
+ "<project-stage>",
)
# YAML block-scalar headers — must not be stored as scalar content,
diff --git a/tools/spec-loop/specs/organization-adapters.md b/tools/spec-loop/specs/organization-adapters.md
new file mode 100644
index 00000000..a9faca8c
--- /dev/null
+++ b/tools/spec-loop/specs/organization-adapters.md
@@ -0,0 +1,125 @@
+<!-- SPDX-License-Identifier: Apache-2.0
+ https://www.apache.org/licenses/LICENSE-2.0 -->
+
+---
+title: Organization adapters (governance + backend defaults grouped per org)
+status: experimental
+kind: feature
+mode: infra
+source: >
+ Extends project-agnosticism.md and adapters.md. An organization
+ adapter groups the governance vocabulary and capability→backend
+ defaults shared by every project under one governing body (foundation,
+ company, maintainer collective), so those defaults live once instead of
+ being copied into each project's manifest. Implemented under
+ organizations/ (organizations/ASF/, organizations/independent/,
+ organizations/_template/).
+acceptance:
+ - An organization's shared defaults (governance vocabulary + capability
+ bundle + infra values) live in organizations/<org>/organization.md,
+ not duplicated across each project's project.md.
+ - A project selects its organization with a single key
+ (organization:<org> in project.md) and inherits the rest.
+ - Config resolves project.md -> organizations/<org>/ -> framework
+ default, first hit wins; skills never branch on the organization.
+ - A new organization adapter can be authored from
+ organizations/_template/ without editing any skill body.
+---
+
+## What it does
+
+Introduces a configuration layer **between** a single project's
+`<project-config>/` and the framework defaults. An **organization
+adapter** captures what a governing organization makes default for all
+its projects:
+
+- **governance vocabulary** — what the governing body is called
+ (`<governance-body>`), how contributors are admitted
+ (`contributor_intake`: ICLA / DCO / none), the project-lifecycle
+ stages (`<project-stage>`), role names; and
+- **capability→backend bundle + infrastructure values** — which tool
+ adapter fulfils each capability (CVE authority, mail archive, project
+ metadata, forwarder, …) and the concrete URLs / addresses those
+ backends use.
+
+This removes the duplication whereby every ASF project re-declared the
+identical "ASF default" values in its own `project.md`.
+
+## Where it lives
+
+- `organizations/README.md` — the entity overview.
+- `organizations/ASF/` — the reference adapter (Apache Software
+ Foundation defaults: PMC governance, Vulnogram, PonyMail,
+ `apache-projects-mcp`, ASF-security forwarder, `*.apache.org` infra).
+- `organizations/independent/` — the no-formal-org baseline (DCO, GHSA,
+ GitHub Releases, no list/forwarder/metadata backends); inherited by
+ `projects/non-asf-example/`.
+- `organizations/_template/` — authoring skeleton.
+- Resolution contract: `AGENTS.md` §"Configuration resolution order".
+- Path exempted from placeholder / asf-coupling lint via
+ `ALLOWLIST_PATHS += "organizations/"` in `skill-and-tool-validator`.
+
+## Behaviour & contract
+
+- A project names its organization once: `organization: <org>` in
+ `<project-config>/project.md` (default `independent`).
+- Every placeholder and dotted config key resolves
+ `project.md → organizations/<org>/organization.md → framework default`,
+ first hit wins. This is the only inheritance in the config model.
+- Skills do **not** read the `organization:` key to branch behaviour;
+ they read capability keys (`cve_authority.tool`, `archive_system.kind`,
+ `<governance-body>`, …) and take the first value the chain yields.
+- An organization adapter declares only org-wide values. Per-project
+ values (security-list address, scope labels, product name, roster,
+ tracker label/body-field vocabulary) stay in `project.md`.
+- The `organizations/ASF/organization.md` keys mirror the namespaces of
+ the project manifest's *Security workflow configuration* section so
+ resolution is mechanical.
+
+## Out of scope
+
+- Removing the ASF default *values* from `projects/_template/project.md`
+ (the reflow to actually inherit) — a follow-up change.
+- Making the `asf:false` skill families fully agnostic (moving residual
+ PMC/ICLA/incubator vocabulary behind placeholders) — a follow-up.
+- Replacing the per-family `asf: true/false` metadata with an optional
+ family-level `organization:` scope — a follow-up.
+- Any runtime fetch/install of externally-defined adapters (discovery
+ only; see PRINCIPLES §13).
+
+## Acceptance criteria
+
+- An organization's shared defaults live in
+ `organizations/<org>/organization.md`, not duplicated per project.
+- `organization: <org>` in `project.md` selects the adapter.
+- Resolution is `project.md → org adapter → framework default`,
+ first hit wins; no skill branches on the organization.
+- A new adapter can be authored from `organizations/_template/` with no
+ skill edits.
+
+## Validation
+
+```bash
+# Validator passes; organizations/ is allowlisted so ASF terms inside
+# organizations/ASF/ do not trip the placeholder / asf-coupling checks.
+uv run --project tools/skill-and-tool-validator skill-and-tool-validate
+
+# Doc + link + placeholder hooks green on the new files.
+prek run --files organizations/**/*.md docs/vendor-neutrality.md AGENTS.md
+```
+
+Manual resolution check: a `project.md` with `organization: ASF` and a
+key omitted resolves to the ASF adapter value; `organization: independent`
+resolves to the baseline.
+
+## Known gaps
+
+- The reflow of `projects/_template/` + `projects/non-asf-example/` to
+ set `organization:` and drop the now-inherited values is not yet done
+ (tracked as the next change); until then the ASF defaults exist in both
+ `projects/_template/project.md` and `organizations/ASF/`.
+- No structural validator check yet enforces required files in
+ `organizations/<org>/` (currently README + organization.md by
+ convention).
+- The family-level `organization:` scope (replacing `asf: true/false`)
+ and the external-adapter discovery index are separate follow-ups.