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.