Skip to content
Open
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
4 changes: 2 additions & 2 deletions docs/labels-and-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,10 @@ or a contract-free mix of substrates (e.g. `tools/spec-inventory` is
| [`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`, `promote` and `verify-rc` |
| [`tools/release-management`](../tools/release-management/) | `substrate:release` | `release-management` — renders what the `release-*` skills hand the release manager (paste-ready command blocks, drafts, planning-issue comments) from `release-config load`, refusing a `dist/release/` staging target or an MD5 / SHA-1 digest in code; runs nothing. Used by `rc-cut` Steps 0 and 2–4, `verify-rc` Steps 1–10, `vote-draft` Steps 0, 2 and 3 (verify-rc gate, `[VOTE]` body, planning-issue comment), `vote-tally` Steps 0–3 (pre-classification, the tally, the `[RESULT] [VOTE]` body), `promote` Steps 0 and 2, `announce-draft` Steps 0, 2 and 3, and `prepare` Steps 0, 1, 2, 14 and A (train coverage, the planning issue, the changelog and its coverage, the 2e archive review, the prep and post-release PRs, the automated-signing gate and workflow) |
| [`tools/release-management`](../tools/release-management/) | `substrate:release` | `release-management` — renders what the `release-*` skills hand the release manager (paste-ready command blocks, drafts, planning-issue comments) from `release-config load`, refusing a `dist/release/` staging target or an MD5 / SHA-1 digest in code; runs nothing. Used by `rc-cut` Steps 0 and 2–4, `verify-rc` Steps 1–10, `vote-draft` Steps 0, 2 and 3 (verify-rc gate, `[VOTE]` body, planning-issue comment), `vote-tally` Steps 0–3 (pre-classification, the tally, the `[RESULT] [VOTE]` body), `promote` Steps 0 and 2, `announce-draft` Steps 0, 2 and 3, `archive-sweep` Steps 0–2 (the listing command, `trains.json`, the retention rule, the backend-shaped archive commands), `audit-report` Steps 0–3 (the planning-issue URL, the mail-archive search plan, voter handles, the record, the PR proposal) `keys-sync` Steps 0–2 (the KEYS lookup and UID comparison over `release-verify list-keys` output, the strength floor, the KEYS block and the svn / git commands), and `prepare` Steps 0, 1, 2, 14 and A (train coverage, the planning issue, the changelog and its coverage, the 2e archive review, the prep and post-release PRs, the automated-signing gate and workflow) |
| [`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/release-verify`](../tools/release-verify/) | `substrate:release` | `release-verify` — deterministic, read-only checks of a staged RC (inventory, signatures against the project KEYS only, checksums, NOTICE/LICENSE, prohibited binaries, dangling symlinks, version strings) and the verdict roll-up, each as JSON; used by `release-verify-rc` Steps 1–3, 5–8 and 10 |
| [`tools/release-verify`](../tools/release-verify/) | `substrate:release` | `release-verify` — deterministic, read-only checks of a staged RC (inventory, signatures against the project KEYS only, checksums, NOTICE/LICENSE, prohibited binaries, dangling symlinks, version strings) and the verdict roll-up, each as JSON; used by `release-verify-rc` Steps 1–3, 5–8 and 10; and `list-keys`, the fingerprints and UIDs of a KEYS file or fetched key, for `release-keys-sync` Steps 0–1 |
| [`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 |
| [`tools/pilot-report-validator`](../tools/pilot-report-validator/) | `substrate:framework-dev` | Adopter pilot-report validator — required frontmatter keys, no unfilled placeholders, valid profile, and required body sections; counterpart to `spec-validator` for `docs/pilot-report-template.md` |
| [`tools/skill-reconciler-diff`](../tools/skill-reconciler-diff/) | `substrate:framework-dev` | Deterministic structural diff between two skill trees — parses frontmatter, section headings, step inventory, placeholders, support files, and safety-baseline clauses into a JSON diff object for the `skill-reconciler` skill |
Expand Down
4 changes: 2 additions & 2 deletions docs/vendor-neutrality.md
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,8 @@ generated block below.

| Skill neutrality | Count |
|---|---|
| capability-pure (names no backend) | 28 |
| portable (named backends are swappable) | 51 |
| capability-pure (names no backend) | 29 |
| portable (named backends are swappable) | 50 |
| vendor-coupled (sole-backend dependency) | 0 |

Organization scope (declared, orthogonal to vendor): ASF = 16, agnostic = 63.
Expand Down
136 changes: 43 additions & 93 deletions plugins/magpie-release-management/skills/archive-sweep/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ capability:
- capability:triage
surface_hash: sha256:1665af8aae9c2b58
license: Apache-2.0
measured_tokens: 4573
measured_tokens: 4165
---

<!-- SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -159,12 +159,8 @@ Local modifications go in the override file; framework changes go via PR to `apa

## Prerequisites

- **`<project-config>/release-management-config.md` readable** — `archive_retention_rule`, `release_dist_backend`, `release_dist_url_template`,
and the archive destination: `archive_url_template`, which defaults to `https://archive.apache.org/dist/<project>/` (from `project_dist_name`) for both ASF backends, `svnpubsub` and `atr`,
and is required for any other backend.
- **`<project-config>/release-trains.md` readable** — the supported release lines and their current latest versions; used to identify orphans.
- **Distribution listing accessible** — the list of releases currently on `dist/release/<project>/` (for `release_dist_backend = svnpubsub`, or the backend equivalent).
For `svnpubsub`, this is an `svn list` call against the distribution URL.
The retention and backend keys, `release-trains.md`, and a readable distribution listing:
the full list is in [`prerequisites.md`](prerequisites.md). Step 0 checks the configuration.

---

Expand All @@ -178,18 +174,21 @@ Local modifications go in the override file; framework changes go via PR to `apa

## Step 0 — Pre-flight check

Run the checks with the [`release-config`](../../../../tools/release-config/README.md) tool:
Steps 0–2 run the [`release-management`](../../../../tools/release-management/README.md) tool, which prints JSON and runs nothing;
`<rm>` stands for `uv run --project <framework>/tools/release-management release-management archive-sweep`.
A command that would emit what this skill refuses exits `2` with `{"ok": false, "refused": "<why>"}`: surface `refused` as written and stop.

Run the checks with the [`release-config`](../../../../tools/release-config/README.md) tool, then fold them into the step's JSON:

```bash
uv run --project <framework>/tools/release-config release-config preflight --skill archive-sweep
uv run --project <framework>/tools/release-config release-config preflight --skill archive-sweep > <scratch>/archive-sweep-preflight.json
<rm> verdict --preflight <scratch>/archive-sweep-preflight.json
```

It covers the required config keys, `release-trains.md` (at least one release line),
the backend and the archive destination (the `archive.apache.org` default for `svnpubsub` and `atr`),
and prints `{"ok", "blockers", "warnings", "values"}`.
Each `blockers` entry is a hard blocker; surface it as written.
Surface `warnings` and carry on.
Copy `non_asf` and `dist_backend` from `values`.
The preflight covers the required config keys, `release-trains.md` (at least one release line),
the backend and the archive destination (the `archive.apache.org` default for `svnpubsub` and `atr`).
`verdict` copies its `blockers` as written and `non_asf` and `dist_backend` from `values`.
Surface the preflight `warnings` and carry on.

Then:

Expand All @@ -216,35 +215,36 @@ Return ONLY valid JSON with this structure:

## Step 1 — Load dist listing and apply retention rule

1. **Fetch the listing.** Read the list of versioned releases currently on the distribution surface:
- `svnpubsub`: `svn list <dist-release-url>` — each directory entry is a version or a version-suffix directory.
- `atr`: the project's release list in ATR, which is also the authoritative record of what has already been archived.
The distribution area itself is still `dist/release/<project>/`, since ATR's Finish commits there.
- `github-releases`: `gh release list --repo <upstream>` — each published (non-draft) release tag is a candidate.
- `s3`: `aws s3 ls s3://<bucket>/<project>/` — each key prefix is a candidate.
- `self-hosted`: the adopter-supplied listing command from `<project-config>/release-management-config.md`.
`<rm>` is as in Step 0. Save the configuration once: `uv run --project <framework>/tools/release-config release-config load --skill archive-sweep > <scratch>/archive-sweep-load.json`.

1. **Fetch the listing.** `<rm> listing --load <scratch>/archive-sweep-load.json [--upstream <upstream>]` prints the backend's read-only listing `command`
(`svn list`, `gh release list`, `aws s3 ls`) or, for `atr` and `self-hosted`, a `note` naming where the list lives;
load the backend file it names in `read`.
Read the listing and save the entries, one per line, to `<scratch>/listing.txt`.

Save the entries, one per line, to `<listing.txt>`.
2. **Write the trains.** `<rm> trains --load <scratch>/archive-sweep-load.json > <scratch>/trains.json` turns each release line of `release-trains.md`
naming one `N.x` train into `{"label": "2.x", "pattern": "2.x"}`; any other line is a `loose` train.
When `keep_decided` is false, `archive_retention_rule` is free text: decide how many versions it keeps per train and re-run with `--keep N`
(or set `"keep"` per train in the file).

2. **Apply the retention rule.** Write the supported trains from `<project-config>/release-trains.md` as JSON —
`{"label": "2.x", "pattern": "2.x"}` each, plus `"keep": N` where `archive_retention_rule` keeps more than the latest — and run:
3. **Apply the retention rule.**

```bash
python3 <skill-dir>/scripts/retention.py --listing <listing.txt> --trains <trains.json>
<rm> retention --listing <scratch>/listing.txt --trains <scratch>/trains.json > <scratch>/retention.json
```

Per train it keeps the newest `keep` (default 1) and marks earlier versions past retention;
releases on no train are `orphans`, never archived.
A pre-release in the release area is listed in `prereleases`, never counted as a train's latest and never archived; it is a hand-off to the RM.
`keep` below 1 would archive a train's latest release:
the script sets `retention_rule_error` and empties `past_retention`, and no archival command may be emitted.
it sets `retention_rule_error` and empties `past_retention`, and no archival command may be emitted.

3. **Place what it could not.** A version in `unmapped` matched a loose pattern or several trains:
decide its train, list it in that train's `"versions"`, and re-run until `mapping_complete` is true.
A rule `keep` cannot express goes to the RM; nothing may drop the latest-of-each-train floor.
4. **Read what fired.** `retention_rule_error` → [`outcomes/retention-rule-error.md`](outcomes/retention-rule-error.md);
`orphans` or `prereleases` → [`outcomes/orphans.md`](outcomes/orphans.md);
`unmapped` (a loose or overlapping train) → [`outcomes/unmapped.md`](outcomes/unmapped.md), then re-run until `mapping_complete` is true.

Surface the classification table to the RM before proceeding to Step 2.
Copy the lists, `latest_of_each_line`, `handoff_required`, and `handoff_reasons` from the script (already in ascending version order, matching Step 2's command order);
Copy the lists, `latest_of_each_line`, `handoff_required`, and `handoff_reasons` from `retention` (already in ascending version order, matching Step 2's command order);
write `retention_rule_summary` yourself.

Return ONLY valid JSON with this structure:
Expand All @@ -268,58 +268,19 @@ When `handoff_required` is `true` for a `retention-rule-error`, `past_retention`

## Step 2 — Emit archive command set

Compose the backend-shaped command set to move each past-retention release from the distribution surface to the archive area.

**`svnpubsub` (ASF default).**
For each past-retention version `<ver>`:

```text
svn mv \ # release_dist_backend=svnpubsub
https://dist.apache.org/repos/dist/release/<project>/<ver> \ # release_dist_backend=svnpubsub
https://archive.apache.org/dist/<project>/<ver> \
-m "Archive <project> <ver> per retention policy"
```

One `svn mv` (for `release_dist_backend = svnpubsub`) per past-retention version, in ascending version order (oldest first).
Include the commit message inline.

**`atr`.**
There is no command to emit.
Archiving happens in ATR, which updates the release catalog and removes the files from `dist/release` in the background —
so the RM performs it in the ATR UI, not in a shell, and the usual "paste-ready command set" output is replaced by the instruction to archive each past-retention version there.

Two consequences worth stating in the proposal:
Render the backend-shaped command set that moves each past-retention release from the distribution surface to the archive area:

- **Do not also run `svn mv` or `svn rm`.** ATR removes the files itself; a manual removal on top races with it.
- **The prior release may already be handled.** If the project enables *Auto archive prior release* in its ATR settings,
the previous release is archived in the same cycle when the new one is announced — so it may not be past-retention by the time this sweep runs.
Check ATR's record before proposing anything.

Releases committed to `dist/release` are copied to `archive.apache.org` automatically, so archiving removes the distribution copy rather than moving it.
See [Promoting to release](https://releases.apache.org/docs/promoting-to-release).

**`github-releases`.**
For each past-retention version `<ver>`:

```text
gh release delete <ver> --repo <upstream> --yes
```

Note: `gh release delete` removes the release page and optionally the tag.
Include a reminder that GitHub releases have no archive equivalent; deletion is permanent.
The RM should confirm this is intentional.

**`s3`.**
For each past-retention version `<ver>`:

```text
aws s3 mv \
s3://<bucket>/<project>/<ver>/ \
s3://<archive-bucket>/<project>/<ver>/ \
--recursive
```bash
uv run --project <framework>/tools/release-management release-management archive-sweep render --load <scratch>/archive-sweep-load.json --retention <scratch>/retention.json \
[--upstream <upstream>] [--command-template '<self-hosted archival command>']
```

**`self-hosted`.** Use the adopter-supplied archival command template from `<project-config>/release-management-config.md`, substituting `<ver>` and the archive destination.
It emits one command per version in `past_retention`, oldest first, and never one for an orphan or a pre-release (`not_archived` names them);
it refuses a `retention_rule_error` or a mapping that is not complete.
Load the files in `read` — the backend's ([`svnpubsub`](backends/svnpubsub.md), [`atr`](backends/atr.md), [`github-releases`](backends/github-releases.md),
[`s3`](backends/s3.md), [`self-hosted`](backends/self-hosted.md)) and any outcome — and show its notes with the commands
(`deletion_warning` for `github-releases`, `atr_note` for `atr`, where there is no command to run and the RM archives in ATR, never also with `svn mv` or `svn rm`).
The skill never runs any of these commands; the RM does, under their own credentials.

Present the command set and ask for the RM's explicit confirmation before recording the proposal.

Expand All @@ -334,6 +295,7 @@ Return ONLY valid JSON with this structure:
}
```

Copy `archive_count`, `commands`, `backend` and `proposed` from `render`.
`proposed` is always `true` at the point this JSON is returned — no archival command has been run.
Execution is the RM's step.

Expand Down Expand Up @@ -373,16 +335,4 @@ The AI-driven part ends with a hand-back artefact containing:

## References

- [`docs/release-management/process.md`](../../../../docs/release-management/process.md) —
Step 12 context.
- [`docs/release-management/spec.md`](../../../../docs/release-management/spec.md) —
`release-archive-sweep` per-skill specification.
- [`<project-config>/release-management-config.md`](../../../magpie-setup/templates/release-management-config.md) —
adopter keys this skill reads (`archive_retention_rule`,
`release_dist_backend`, `release_dist_url_template`).
- `release-announce-draft` — upstream step (Step 11).
- `release-audit-report` (proposed) — downstream step (Step 13).
- [ASF release distribution § archiving](https://infra.apache.org/release-distribution.html) —
the retention baseline ("only the latest version of each supported line").
- [archive.apache.org](https://archive.apache.org/dist/) — the ASF archive
destination.
Config keys, upstream and downstream skills, ASF distribution policy and the tools: [`references.md`](references.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/licenses/LICENSE-2.0 -->

# Archive backend — `atr`

**Listing.** The project's release list in ATR, which is also the authoritative record of what has already been archived.
The distribution area itself is still `dist/release/<project>/`, since ATR's Finish commits there.

**Archive.** There is no command to emit.
Archiving happens in ATR, which updates the release catalog and removes the files from `dist/release` in the background —
so the RM performs it in the ATR UI, not in a shell, and the usual "paste-ready command set" output is replaced by the instruction to archive each past-retention version there.
`render` prints that instruction as `commands` and `archive_count` is the number of versions to archive in ATR.

Two consequences worth stating in the proposal:

- **Do not also run `svn mv` or `svn rm`.** ATR removes the files itself; a manual removal on top races with it.
- **The prior release may already be handled.** If the project enables *Auto archive prior release* in its ATR settings,
the previous release is archived in the same cycle when the new one is announced — so it may not be past-retention by the time this sweep runs.
Check ATR's record before proposing anything.

Releases committed to `dist/release` are copied to `archive.apache.org` automatically, so archiving removes the distribution copy rather than moving it.
See [Promoting to release](https://releases.apache.org/docs/promoting-to-release).
Loading
Loading