Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ substrate:release:
- changed-files:
- any-glob-to-any-file:
- 'tools/maven-artifact-verify/**'
- 'tools/release-config/**'
- 'tools/reproducible-archive/**'

substrate:review:
Expand Down
3 changes: 2 additions & 1 deletion docs/labels-and-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ framework substrate:
| `substrate:action-guard` | substrate | Deterministic pre-tool-use command guards. |
| `substrate:privacy` | substrate | PII redaction / approved-LLM gating. |
| `substrate:framework-dev` | substrate | Build / validate / eval the framework itself. |
| `substrate:release` | substrate | Release-artefact helpers an adopter's release process runs: reproducible-archive build, lint and comparison; maven-artifact-verify for staged JVM artefacts. |
| `substrate:release` | substrate | Release-artefact helpers an adopter's release process runs: reproducible-archive build, lint and comparison; maven-artifact-verify for staged JVM artefacts; release-config loading and pre-flight checks. |
| `substrate:setup` | substrate | Adopter-side setup state an agent resolves at runtime rather than at development time: lock parsing, floor comparison, reconciliation fingerprints. |
| `substrate:review` | substrate | A second model's read of a change before it is published: other models' CLIs run read-only, findings merged. |

Expand Down Expand Up @@ -362,6 +362,7 @@ or a contract-free mix of substrates (e.g. `tools/spec-inventory` is
| [`tools/spec-status-index`](../tools/spec-status-index/) | `substrate:framework-dev` + `substrate:analytics` | Index of spec / RFC implementation status — framework-dev substrate that also doubles as a governance/stats view (`analytics`) |
| [`tools/vendor-neutrality-score`](../tools/vendor-neutrality-score/) | `substrate:framework-dev` + `substrate:analytics` | Deterministic vendor-neutrality score — reads each contract tool's `**Kind:**` / `**Vendor:**` metadata and scores per-contract + per-skill neutrality (`analytics`); backs the score block in [`docs/vendor-neutrality.md`](vendor-neutrality.md) |
| [`tools/spec-validator`](../tools/spec-validator/) | `substrate:framework-dev` | Spec-frontmatter and body-section validator — counterpart to `skill-and-tool-validator` for `tools/spec-loop/specs/` |
| [`tools/release-config`](../tools/release-config/) | `substrate:release` | `release-config` — loads the release-management config (`<project-config>` layered lookup, defaults, derived values) and runs each `release-*` skill's deterministic Step 0 pre-flight checks as JSON; used by every `release-*` skill's Step 0 and the Step 1 metadata of `rc-cut`, `vote-draft`, `announce-draft` and `promote` |
| [`tools/reproducible-archive`](../tools/reproducible-archive/) | `substrate:release` | `repro-archive` — build, lint and compare reproducible source archives from a git ref (`git archive` + `.gitattributes` `export-ignore`, every reproducible-builds.org archive rule applied); used by `release-rc-cut` Step 2/2b and `release-verify-rc` Step 9 |
| [`tools/maven-artifact-verify`](../tools/maven-artifact-verify/) | `substrate:release` | `maven-artifact-verify` — verify locally staged JVM RC artefacts: POM ALv2 licence / developers / scm (with parent-POM inheritance resolution), podling incubation disclaimer in `<description>`, companion `-sources.jar` / `-javadoc.jar` presence with their own signatures and checksums; used by `release-verify-rc` Step 6b |
| [`tools/symlink-lint`](../tools/symlink-lint/) | `substrate:framework-dev` | Self-adoption symlink hygiene — rejects cyclic symlinks, misdirected skill relays (canonical/relay target-correctness), and incomplete self-adoption symlink sets |
Expand Down
7 changes: 4 additions & 3 deletions docs/release-management/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -856,14 +856,15 @@ backend table in
[`projects/_template/release-management-config.md`](../../plugins/magpie-setup/templates/release-management-config.md))
are required only when the corresponding backend is selected.

ASF TLP releases are pinned to `release_approval_mechanism =
Releases of an ASF project (`project.md` → `organization: ASF`) are
pinned to `release_approval_mechanism =
dev-list-vote` (mandatory per
[`release-policy.html § release approval`](https://www.apache.org/legal/release-policy.html#release-approval))
and `release_announce_backend = announce-list` (mandatory per
[`release-policy.html § announcements`](https://www.apache.org/legal/release-policy.html#release-announcements)).
`release-vote-tally` and `release-announce-draft` refuse to run an
ASF TLP release against any other value; non-ASF adopters set the
keys their workflow uses.
ASF release against any other value; non-ASF adopters set the
keys their workflow uses (and may not use `announce-list`).

## Eval

Expand Down
5 changes: 3 additions & 2 deletions docs/vendor-neutrality.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ Organization scope (declared, orthogonal to vendor): ASF = 16, agnostic = 62.

**LLM / agent-integration neutrality**

**Agent harness: 30/30 substrate tools run under any harness unchanged (100%).** Substrate tools are Magpie's own machinery; each declares the agent harness it integrates with (`**Harness:**`), or `agnostic`. A tool is neutral when it is harness-agnostic or supports two or more harnesses; *coupled* when it targets a single harness.
**Agent harness: 31/31 substrate tools run under any harness unchanged (100%).** Substrate tools are Magpie's own machinery; each declares the agent harness it integrates with (`**Harness:**`), or `agnostic`. A tool is neutral when it is harness-agnostic or supports two or more harnesses; *coupled* when it targets a single harness.

| Substrate tool | Substrate | Harness support | Verdict |
|---|---|---|---|
Expand All @@ -622,6 +622,7 @@ Organization scope (declared, orthogonal to vendor): ASF = 16, agnostic = 62.
| `preflight-audit` | analytics | any | ✅ agnostic |
| `privacy-llm` | privacy | any | ✅ agnostic |
| `probe-templates` | sandbox | any | ✅ agnostic |
| `release-config` | release | any | ✅ agnostic |
| `reproducible-archive` | release | any | ✅ agnostic |
| `sandbox-lint` | sandbox | Claude Code, Codex, Cursor, Gemini CLI, Kiro, OpenCode | ✅ portable |
| `security-tracker-stats-dashboard` | analytics | any | ✅ agnostic |
Expand All @@ -646,7 +647,7 @@ Harness → substrate tools it supports:
- **Gemini CLI** (3): `agent-guard`, `sandbox-lint`, `spec-loop`
- **Kiro** (3): `agent-guard`, `sandbox-lint`, `spec-loop`
- **OpenCode** (3): `agent-guard`, `sandbox-lint`, `spec-loop`
- **any harness** (27): `adversarial-review`, `agent-isolation`, `container-gateway`, `contributor-metrics`, `dashboard-generator`, `dev`, `egress-gateway`, `maven-artifact-verify`, `permission-audit`, `pilot-report-validator`, `pr-management-stats`, `preflight-audit`, `privacy-llm`, `probe-templates`, `reproducible-archive`, `security-tracker-stats-dashboard`, `setup-preflight`, `skill-and-tool-validator`, `skill-evals`, `skill-reconciler-diff`, `skill-token-count`, `spec-inventory`, `spec-status-index`, `spec-validator`, `symlink-lint`, `vendor-neutrality-score`, `vetted-ops`
- **any harness** (28): `adversarial-review`, `agent-isolation`, `container-gateway`, `contributor-metrics`, `dashboard-generator`, `dev`, `egress-gateway`, `maven-artifact-verify`, `permission-audit`, `pilot-report-validator`, `pr-management-stats`, `preflight-audit`, `privacy-llm`, `probe-templates`, `release-config`, `reproducible-archive`, `security-tracker-stats-dashboard`, `setup-preflight`, `skill-and-tool-validator`, `skill-evals`, `skill-reconciler-diff`, `skill-token-count`, `spec-inventory`, `spec-status-index`, `spec-validator`, `symlink-lint`, `vendor-neutrality-score`, `vetted-ops`

**Model endpoint: neutral by construction — 4 default-approved endpoint classes across independent trust domains, plus adopter opt-in.** From the [`privacy-llm` registry](../tools/privacy-llm/models.md): the framework keys approval on *endpoint identity*, not on who hosts the model, so no single LLM vendor is privileged.

Expand Down
111 changes: 64 additions & 47 deletions plugins/magpie-release-management/skills/announce-draft/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ argument-hint: "<version> [--planning-issue <url>]"
capability: capability:resolve
surface_hash: sha256:edffafcd9d9948ab
license: Apache-2.0
measured_tokens: 6928
measured_tokens: 6925
---

<!-- SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -166,12 +166,14 @@ the skill surfaces it as a scope violation and asks the RM to confirm
before including it.

**Golden rule 7 — ASF TLP backend enforcement.** For an ASF TLP release
(`release_announce_backend = announce-list` is the only legal value per
(a project whose `project.md` declares `organization: ASF`;
`release_announce_backend = announce-list` is the only legal value per
[release-policy.html § announcements](https://www.apache.org/legal/release-policy.html#release-announcements)),
the skill refuses to run against any other `release_announce_backend`
value unless `--non-asf` is passed. Non-ASF adopters pass `--non-asf`
explicitly; the skill then emits backend-shaped artefacts rather than the
ASF `[ANNOUNCE]` format.
value. For every other organization `non_asf` is true (derived from
`organization`, never from a flag): `announce-list` is refused, and the
skill emits backend-shaped artefacts rather than the ASF `[ANNOUNCE]`
format.

---

Expand Down Expand Up @@ -210,41 +212,50 @@ override file. Framework changes go via PR to

| Selector | Resolves to |
|---|---|
| `<version>` (positional) | Release version string to announce |
| `<version>` (positional) | Release version string to announce (a dotted version of two or more numeric parts, no `.postN`, e.g. `2.11.0`) |
| `--planning-issue <url>` | Explicit planning issue URL (auto-detected if omitted) |
| `--promote-timestamp <ISO-8601>` | Override promote timestamp (when not in planning issue body) |
| `--download-page <url>` | Override or supply the canonical Download Page URL |
| `--skip-promote-wait <reason>` | Override the one-hour promote gate; reason is logged in both outputs |
| `--non-asf` | Signal that this is a non-ASF adopter; backend-shaped artefacts emitted instead of ASF `[ANNOUNCE]` format |

---

## Step 0 — Pre-flight check

1. **Version argument parseable.** `<version>` matches the expected
semver-ish pattern (`X.Y.Z` or `X.Y.Z.post0`).
2. **Planning issue found and carries `promoted`.** Either
`--planning-issue <url>` was passed or the skill can find a `promoted`
planning issue on `<upstream>` matching `<version>` in its title.
3. **`release-management-config.md` readable.** The required keys
(`announce_list`, `announce_subject_template`) are present.
4. **Backend enforcement.** For ASF TLPs (`release_announce_backend =
announce-list`), `--non-asf` must NOT be present. For non-`announce-list`
backends in an ASF TLP context, the skill stops unless `--non-asf` was
passed.
5. **Promote timestamp available.** The planning issue body contains a
promote timestamp, or `--promote-timestamp <ISO-8601>` was passed.
6. **Promote wait gate.** Current time is at least one hour after the
promote timestamp, or `--skip-promote-wait <reason>` was passed.
7. **Download Page URL available.** The URL is present in the planning
issue body, the config file, or via `--download-page <url>`.
8. **Drift check** — the generated pre-flight block reports snapshot drift.
9. **Override consultation** — see *Adopter overrides* above.
First find the planning issue: either `--planning-issue <url>` was
passed or the skill can find a planning issue on `<upstream>` matching
`<version>` in its title.
Read its promote timestamp and Download Page URL, if present, and pass
them to the [`release-config`](../../../../tools/release-config/README.md)
tool with the RM's arguments (a flag the RM passed wins):

```bash
uv run --project <framework>/tools/release-config release-config preflight \
--skill announce-draft <version> [--promote-timestamp <ISO-8601>] \
[--download-page <url>] [--skip-promote-wait <reason>]
```

It covers the version format, the required config keys, the
announce-backend enforcement (an ASF project — `project.md` →
`organization: ASF` — announces on `announce-list`, and only an ASF
project may), the promote timestamp, the one-hour
promote-wait gate and the Download Page URL, and prints
`{"ok", "blockers", "warnings", "values"}`.
Each `blockers` entry is a hard blocker; surface it as written — the
promote-wait blocker names the exact UTC time the gate clears.
Surface `warnings` and carry on.
Copy `skip_promote_wait_override`, `non_asf` and
`promote_clear_after_utc` from `values`; `non_asf` is true unless
`project.md` declares `organization: ASF`.

Then check what the tool cannot see:

1. **Planning issue found and carries `promoted`.** Without it, block.
2. **Drift check** — the generated pre-flight block reports snapshot drift.
3. **Override consultation** — see *Adopter overrides* above.

If any check fails (and is not overridden), stop and surface what is
missing with the exact UTC time after which the gate clears (for the
promote-wait check), or the exact key name that is missing (for config
checks).
missing.

Return ONLY valid JSON with this structure:

Expand All @@ -262,29 +273,35 @@ Return ONLY valid JSON with this structure:
`promote_clear_after_utc` field is non-null when the promote-wait gate
is the only blocker; it gives the exact UTC moment after which the skill
will proceed without `--skip-promote-wait`.
The tool sets it only when the gate is its sole blocker; report `null`
when the planning-issue check blocks too.

---

## Step 1 — Load release metadata

Read the following from the planning issue body and
`<project-config>/release-management-config.md`:
Load the config-derived fields with the same tool, passing the promote
timestamp Step 0 used:

```bash
uv run --project <framework>/tools/release-config release-config load \
--skill announce-draft <version> --promote-timestamp <ISO-8601>
```

Its `metadata` carries `version`, `promote_timestamp` (UTC), `keys_url`,
`announce_list`, `announce_cc_lists`, `subject_template`, `site_repo`
(may be absent for non-site backends), `site_pr_files` (with
`<version>` rendered) and `release_announce_backend`.

Read the rest from the planning issue body, Step 0 and the canned
responses:

| Metadata field | Source | Key / location |
|---|---|---|
| `product_name` | `release-management-config.md` | derived from `project_dist_name` (capitalised display name) |
| `version` | trigger argument | `<version>` |
| `promote_timestamp` | planning issue body or `--promote-timestamp` | UTC ISO-8601 timestamp of Step 10 promote commit |
| `dist_release_url` | planning issue body | URL under `dist/release/<project>/<version>/` (for `release_dist_backend = svnpubsub`) |
| `download_page_url` | planning issue body, config, or `--download-page` | canonical Download Page URL |
| `download_page_url` | Step 0 `values.download_page_url` | canonical Download Page URL (planning issue body, config, or `--download-page`) |
| `changelog_url` | planning issue body | URL to changelog for this release |
| `keys_url` | `release-management-config.md` | `keys_file_url` |
| `announce_list` | `release-management-config.md` | `announce_list` |
| `announce_cc_lists` | `release-management-config.md` | `announce_cc_lists` |
| `subject_template` | `release-management-config.md` | `announce_subject_template` |
| `site_repo` | `release-management-config.md` | `site_repo` (may be absent for non-site backends) |
| `site_pr_files` | `release-management-config.md` | `site_pr_files` list |
| `release_announce_backend` | `release-management-config.md` | `release_announce_backend` |
| `canned_body` | `<project-config>/canned-responses.md` | `[ANNOUNCE]` template block, if present |

Surface the loaded metadata to the RM for confirmation before
Expand Down Expand Up @@ -313,7 +330,7 @@ Cc: <announce_cc_lists joined by ", ">
Subject: [ANNOUNCE] <Product Name> <version> released

NOTE: This email must be sent from your @apache.org address. The
<announce-list> rejects non-@apache.org senders (for ASF TLPs).
<announce-list> rejects non-@apache.org senders (for ASF projects).

The Apache <Project Name> community is pleased to announce the release
of <Product Name> <version>.
Expand Down Expand Up @@ -343,7 +360,7 @@ above routes through the CDN/mirror selector (closer.lua).>
accepted this with the reason: <reason>.] ← include only when --skip-promote-wait
```

**Non-ASF backend variants.** When `--non-asf` is passed, substitute the
**Non-ASF backend variants.** When `non_asf` is true, substitute the
backend-appropriate shape per the `release_announce_backend` value:

- `github-release-notes`: a GitHub Release page body (no `To:` / `Cc:`
Expand Down Expand Up @@ -554,8 +571,8 @@ The AI-driven part ends with a hand-back artefact containing:
URL instead.
- **Never announce before the one-hour promote gate** unless
`--skip-promote-wait <reason>` was passed.
- **Never run with a non-`announce-list` backend for an ASF TLP release**
unless `--non-asf` was explicitly passed.
- **Never run with a non-`announce-list` backend for an ASF project**
(`project.md` → `organization: ASF`).
- **Never invent metadata.** All dist URLs, download page URLs, changelog
URLs, and keys URLs must come from the planning issue body or the
project config. Do not derive or guess paths.
Expand All @@ -568,7 +585,7 @@ The AI-driven part ends with a hand-back artefact containing:
|---|---|---|
| Pre-flight blocked — not promoted | Planning issue lacks `promoted` label | Complete Step 10 (`release-promote`), or supply `--planning-issue` pointing at a promoted issue |
| Pre-flight blocked — promote-wait | Promote commit is less than one hour ago | Wait until `promote_clear_after_utc`, or pass `--skip-promote-wait <reason>` |
| Pre-flight blocked — backend mismatch | ASF TLP configured with non-list backend | Fix `release_announce_backend` in config, or pass `--non-asf` for a non-ASF adopter |
| Pre-flight blocked — backend mismatch | An ASF project configured with a non-list backend, or a non-ASF project with `announce-list` | Fix `release_announce_backend` in config, or correct `organization` in `project.md` |
| Download Page URL missing | Not in planning issue or config | Supply via `--download-page <url>` |
| Site-bump PR scope violation | A proposed file is not in `site_pr_files` | Confirm the extra file explicitly or remove it from the site bump |
| `site_repo` missing | Config has no `site_repo` key | Add `site_repo` to `release-management-config.md`, or skip the site bump |
Expand All @@ -592,6 +609,6 @@ The AI-driven part ends with a hand-back artefact containing:
- `release-audit-report` (proposed) — downstream step; records the
complete lifecycle.
- [ASF release policy § announcements](https://www.apache.org/legal/release-policy.html#release-announcements) —
the `<announce-list>` requirement for ASF TLP releases (see `release_announce_backend`).
the `<announce-list>` requirement for ASF releases (see `release_announce_backend`).
- [ASF release distribution](https://infra.apache.org/release-distribution.html) —
the `closer.lua` CDN/mirror selector requirement for download links.
Loading
Loading