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:11 UTC
(magpie) branch feat/organization-adapters created (now bfa7773c)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a change to branch feat/organization-adapters
in repository https://gitbox.apache.org/repos/asf/magpie.git
at bfa7773c feat(organizations): introduce Organization-adapter entity + document adapters
This branch includes the following new commits:
new bfa7773c feat(organizations): introduce Organization-adapter entity + document adapters
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(magpie) 01/01: feat(organizations): introduce Organization-adapter entity + document adapters
Posted by po...@apache.org.
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.