Skip to content

Commit 8a2a3d0

Browse files
authored
Merge pull request #62 from egohygiene/codex/aether-repository-release-contract
spec: define organization-wide repository release contract
2 parents 42c5867 + 5f4158e commit 8a2a3d0

40 files changed

Lines changed: 2476 additions & 25 deletions

.egohygiene/release.json

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"$schema": "https://egohygiene.io/schemas/aether/repository-release/v1.json",
3+
"schema_version": "egohygiene.repository-release/v1",
4+
"repository": {
5+
"id": "egohygiene/aether",
6+
"lifecycle": "active",
7+
"release_profile": "contract"
8+
},
9+
"release": {
10+
"state": "unreleased",
11+
"tag_prefix": "v",
12+
"immutable_tags": true,
13+
"major_alias": "optional"
14+
},
15+
"changelog": {
16+
"path": "CHANGELOG.md",
17+
"format": "keep-a-changelog/1.1",
18+
"unreleased_heading": "Unreleased"
19+
},
20+
"components": [
21+
{
22+
"id": "first-party-artifacts",
23+
"kind": "catalog",
24+
"version_authority": {
25+
"kind": "catalog-record",
26+
"path": "catalog/first-party/catalog.v1.json",
27+
"selector": "artifacts[*].artifact_version"
28+
}
29+
}
30+
],
31+
"delivery": {
32+
"channels": [
33+
{
34+
"kind": "github-release",
35+
"state": "configured",
36+
"relay_profile": "unavailable",
37+
"notes": "Aether's release workflow publishes first-party skill evidence; Relay profile composition is planned."
38+
}
39+
]
40+
},
41+
"evidence": {
42+
"source": "required",
43+
"change": "required",
44+
"provenance": "required",
45+
"sbom": "unavailable",
46+
"signature": "unavailable",
47+
"rollback": {
48+
"strategy": "revert-and-successor-tag",
49+
"instructions": "Revert the faulty default-branch change and publish a corrected successor tag; do not replace an immutable tag or release asset."
50+
}
51+
},
52+
"automation": {
53+
"taskfile_path": "Taskfile.yml",
54+
"tasks": {
55+
"plan": "release:plan",
56+
"prepare": "release:prepare",
57+
"verify": "release:verify",
58+
"publish": "release:publish"
59+
},
60+
"github": {
61+
"manual_dispatch_required": true,
62+
"workflow_path": ".github/workflows/release-first-party-skills.yml",
63+
"state": "configured"
64+
}
65+
}
66+
}

.github/copilot-instructions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ Do **not** treat `.staging/`, `dist/`, or any other path as canonical source.
4545
canonical
4646
[`decision-impact.AGENTS.md`](../library/organization/projections/templates/decision-impact.AGENTS.md)
4747
module by reference; do not copy or weaken its Hygiene contract pins.
48+
8. **Follow the repository release declaration before changing versioning or
49+
publication behavior.** Read [`AGENTS.md`](../AGENTS.md),
50+
[`.egohygiene/release.json`](../.egohygiene/release.json), and the
51+
[`repository-release`](../library/organization/specs/release/repository-release.spec.md)
52+
specification. Release preparation is reviewable; publishing remains an
53+
explicit manual, repository-owned action.
4854

4955
---
5056

.github/workflows/release-first-party-skills.yml

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
name: Release first-party skills
22

33
on:
4-
push:
5-
tags:
6-
- "v*"
74
workflow_dispatch:
85
inputs:
96
release_tag:
@@ -15,7 +12,7 @@ permissions:
1512
contents: read
1613

1714
concurrency:
18-
group: release-first-party-skills-${{ github.event.inputs.release_tag || github.ref_name }}
15+
group: release-first-party-skills-${{ github.event.inputs.release_tag }}
1916
cancel-in-progress: false
2017

2118
jobs:
@@ -37,17 +34,17 @@ jobs:
3734
id: release-context
3835
env:
3936
INPUT_RELEASE_TAG: ${{ github.event.inputs.release_tag || '' }}
37+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
4038
REF_NAME: ${{ github.ref_name }}
41-
EVENT_NAME: ${{ github.event_name }}
4239
run: |
4340
set -euo pipefail
44-
if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
45-
release_tag="${INPUT_RELEASE_TAG}"
46-
else
47-
release_tag="${REF_NAME}"
41+
if [ "${REF_NAME}" != "${DEFAULT_BRANCH}" ]; then
42+
echo "Dispatch this workflow from the default branch (${DEFAULT_BRANCH})." >&2
43+
exit 1
4844
fi
49-
if [[ ! "${release_tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then
50-
echo "Release tag must match v<major>.<minor>.<patch>[suffix]" >&2
45+
release_tag="${INPUT_RELEASE_TAG}"
46+
if [[ ! "${release_tag}" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
47+
echo "Release tag must match exact v<major>.<minor>.<patch>." >&2
5148
exit 1
5249
fi
5350
echo "release_tag=${release_tag}" >> "${GITHUB_OUTPUT}"
@@ -59,6 +56,14 @@ jobs:
5956
ref: ${{ steps.release-context.outputs.release_ref }}
6057
fetch-depth: 0
6158

59+
- name: Require tag on default-branch history
60+
env:
61+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
62+
run: |
63+
set -euo pipefail
64+
git fetch origin "${DEFAULT_BRANCH}"
65+
git merge-base --is-ancestor "$(git rev-parse HEAD)" "origin/${DEFAULT_BRANCH}"
66+
6267
- name: Set up Python
6368
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
6469
with:
@@ -190,14 +195,14 @@ jobs:
190195
-C dist skills github release
191196
echo "path=${archive_path}" >> "${GITHUB_OUTPUT}"
192197
193-
- name: Publish or reuse GitHub release
198+
- name: Publish a new GitHub release
194199
run: |
195200
set -euo pipefail
196201
if gh release view "${RELEASE_TAG}" >/dev/null 2>&1; then
197-
echo "Release ${RELEASE_TAG} already exists; skipping gh skill publish." | tee "${RUN_REPORT_DIR}/publish.txt"
198-
else
199-
gh skill publish "dist" --tag "${RELEASE_TAG}" | tee "${RUN_REPORT_DIR}/publish.txt"
202+
echo "Release ${RELEASE_TAG} already exists; immutable release publication refuses to continue." >&2
203+
exit 1
200204
fi
205+
gh skill publish "dist" --tag "${RELEASE_TAG}" | tee "${RUN_REPORT_DIR}/publish.txt"
201206
202207
- name: Attach release metadata assets
203208
run: |
@@ -208,8 +213,7 @@ jobs:
208213
dist/release/release-provenance.v1.json \
209214
dist/release/LICENSE.notices.txt \
210215
dist/release/release-notes.md \
211-
"${{ steps.archive.outputs.path }}" \
212-
--clobber | tee "${RUN_REPORT_DIR}/release-upload.txt"
216+
"${{ steps.archive.outputs.path }}" | tee "${RUN_REPORT_DIR}/release-upload.txt"
213217
214218
- name: Attest release archive provenance
215219
if: success()

.tasks/release.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
# Repository-owned handoffs required by repository-release/v1. These commands
3+
# do not create a tag, publish an artifact, or use external credentials.
4+
5+
version: "3"
6+
7+
tasks:
8+
release:plan:
9+
desc: Validate Aether's release declaration and print a read-only plan
10+
cmds:
11+
- python3 "library/organization/specs/release/validate.py" --repository "." --format "json"
12+
13+
release:prepare:
14+
desc: Preflight a reviewed Aether release candidate without mutating it
15+
requires:
16+
vars:
17+
- RELEASE_VERSION
18+
cmds:
19+
- python3 "library/organization/specs/release/validate.py" --repository "." --release-version "{{.RELEASE_VERSION}}"
20+
- echo "Prepare {{.RELEASE_VERSION}} in a reviewed release PR by promoting CHANGELOG.md Unreleased content and updating declared authorities."
21+
22+
release:verify:
23+
desc: Verify a reviewed Aether release candidate before manual publication
24+
requires:
25+
vars:
26+
- RELEASE_VERSION
27+
cmds:
28+
- python3 "library/organization/specs/release/validate.py" --repository "." --release-version "{{.RELEASE_VERSION}}" --format "json"
29+
30+
release:publish:
31+
desc: Print the explicit manual release handoff after verification
32+
requires:
33+
vars:
34+
- RELEASE_VERSION
35+
cmds:
36+
- task: release:verify
37+
vars:
38+
RELEASE_VERSION: "{{.RELEASE_VERSION}}"
39+
- echo "Handoff only: create the immutable {{.RELEASE_VERSION}} tag from reviewed default-branch history, then manually dispatch .github/workflows/release-first-party-skills.yml."

AGENTS.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Aether agent guidance
2+
3+
Read this file before changing Aether. Canonical source is under
4+
`library/organization/` and `catalog/`; `.staging/` and `dist/` are derived or
5+
migration evidence, not source.
6+
7+
## Release convention
8+
9+
Every active Ego Hygiene repository follows the versioned
10+
[`repository-release`](library/organization/specs/release/repository-release.spec.md)
11+
contract. Its local declaration is `.egohygiene/release.json`; its root
12+
`CHANGELOG.md` retains an exact `## [Unreleased]` section.
13+
14+
Before changing release behavior, inspect the declaration, version authority,
15+
changelog, existing immutable tags, and repository-owned release workflow.
16+
Use `release:plan`, `release:prepare`, and `release:verify` to make a reviewed
17+
candidate. `release:publish` is an explicit handoff only: do not create tags,
18+
publish packages, deploy sites, use registry credentials, or overwrite release
19+
assets unless the user separately authorizes that exact external action.
20+
21+
Commit messages may inform a semantic-version recommendation, but the reviewed
22+
release plan and declared version authority decide the version. Follow the
23+
shared Conventional Commit work in `egohygiene/egohygiene#284` without letting
24+
it silently rewrite a release.
25+
26+
## Aether boundaries
27+
28+
Aether owns provider-neutral specifications, schemas, skills, and agent
29+
guidance. Relay owns reusable immutable execution; Hygiene owns applicability
30+
policy; Egolint owns conformance validation; Pace observes adoption before it
31+
proposes remediation. Keep provider credentials and delivery adapters in their
32+
consumer repository.
33+
34+
Run the relevant deterministic validation and regenerate `dist/` and the
35+
first-party catalog after changing canonical source.

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ This repository follows release-tag based versioning for install pinning and rel
88
- Installed skill pinning should use repository tags (for example `--pin "v1.0.0"`).
99
- Artifact-level versions remain in frontmatter/catalog metadata and may evolve independently of repository tag cadence.
1010

11-
## Unreleased
11+
## [Unreleased]
1212

13+
- Added the versioned organization-wide repository release and changelog
14+
contract, schema, migration guide, portable authoring skill, Aether
15+
declaration, and safe Taskfile handoffs (#61).
1316
- Added the review-gated social campaign handoff specification, schema, and portable skill with exact Identity/catalog locks, candidate-only drafts, freshness and checklist gates, digest-bound human approval, external-adapter receipts, deterministic examples, and adversarial tests (#53).
1417
- Documentation overhaul for product-level README, contributor workflow, provenance, release/pinning, and safety guidance.
1518
- Added read-only PR validation and explicit tagged first-party skill release workflows with deterministic rebuild checks and release metadata generation.

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ Generated artifacts:
4444
## 4) First-party catalog summary
4545

4646
Current canonical inventory snapshot (recompute with the commands shown):
47-
- 25 specifications (`find library/organization/specs -name "*.spec.md"`)
48-
- 31 skills (`find library/organization/skills -name "SKILL.md"`)
47+
- 27 specifications (`find library/organization/specs -name "*.spec.md"`)
48+
- 33 skills (`find library/organization/skills -name "SKILL.md"`)
4949
- 9 canonical agent profiles (`find library/organization/agents -name "AGENT.md"`)
5050

5151
Machine-readable catalog and provenance:
@@ -221,6 +221,12 @@ At present, GitHub CLI exposes `update` but no dedicated `remove` subcommand; re
221221

222222
See `CHANGELOG.md` and `docs/release-and-pinning-guide.md`.
223223

224+
Every active Ego Hygiene repository also declares its repository-level release
225+
profile, version authorities, changelog, evidence, and manual handoff in
226+
`.egohygiene/release.json`. The canonical convention and migration guide are
227+
[`repository-release`](library/organization/specs/release/repository-release.spec.md)
228+
and [repository release convention](docs/repository-release-convention.md).
229+
224230
## 13) External-skill provenance policy
225231

226232
External skill records must be reconstructable from staged provenance evidence and validated against schema:

Taskfile.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ version: "3"
66
# Keep Taskfile as an ergonomic orchestration layer. Canonical implementation
77
# remains in ./aether, GitHub CLI, and the repository's versioned build scripts.
88
includes:
9+
release:
10+
taskfile: ./.tasks/release.yml
11+
flatten: true
912
skills:
1013
taskfile: ./.tasks/skills.yml
1114
flatten: true

catalog/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ provenance, lifecycle, and distribution.
99
- `fixtures/` — valid/invalid examples per schema
1010
- `schemas/aether.social-campaign-handoff.v1.schema.json` — closed review,
1111
approval, immutable-export, and external-publication receipt contract
12+
- `schemas/aether.repository-release.v1.schema.json` — repository profile,
13+
semantic-version, changelog, delivery, evidence, and manual-handoff contract
1214
- `first-party/catalog.v1.json` — canonical compatibility catalog for the current specification and skill corpus
1315
- `external/approved-skills.v1.json` — governed external skill catalog entries reconstructed from staged provenance
1416
- `external/source-candidates.v1.json` — non-publishable external source candidates, including their rights-review state

0 commit comments

Comments
 (0)