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
129 changes: 129 additions & 0 deletions .continuum/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
schema: 1
repo:
name: wesley
owner: flyingrobots
primary_artifact: rust-crates
public_binary: wesley
product_boundary: domain-free-graphql-to-ir

versioning:
strategy: semver
tag_format: 'v{version}'
release_branch_format: 'release/v{version}'
release_milestone_format: 'Release: v{version}'
Comment thread
flyingrobots marked this conversation as resolved.
release_lane_label_format: 'v{version}'
goalpost_milestone_format: 'Goalpost: {name}'

version_sources:
- path: crates/wesley-core/Cargo.toml
Comment thread
flyingrobots marked this conversation as resolved.
name: wesley-core
type: cargo-manifest
field: package.version
required: true
published: true
- path: crates/wesley-emit-codec/Cargo.toml
name: wesley-emit-codec
type: cargo-manifest
field: package.version
required: true
published: true
- path: crates/wesley-emit-rust/Cargo.toml
name: wesley-emit-rust
type: cargo-manifest
field: package.version
required: true
published: true
- path: crates/wesley-emit-typescript/Cargo.toml
name: wesley-emit-typescript
type: cargo-manifest
field: package.version
required: true
published: true
- path: crates/wesley-cli/Cargo.toml
name: wesley-cli
type: cargo-manifest
field: package.version
required: true
published: true
- path: crates/wesley-holmes/Cargo.toml
name: wesley-holmes
type: cargo-manifest
field: package.version
required: true
published: false
- path: package.json
name: root-private-package
type: json
field: version
required: true
published: false

docs:
changelog: CHANGELOG.md
front_door:
- README.md
- docs/README.md
- docs/GUIDE.md
- docs/ENTRYPOINTS.md
architecture:
- docs/ARCHITECTURE.md
- docs/TECHNICAL_TEARDOWN.md
user_docs:
- docs/GUIDE.md
- docs/site/
- docs/topics/
- docs/reference/
- docs/releases/
Comment thread
flyingrobots marked this conversation as resolved.
operator_docs:
- docs/method/release.md
- docs/method/release-runbook.md
- docs/CRATES_IO_RELEASE.md
- docs/governance/RELEASE_POLICY.md
- docs/governance/RELEASE_CHECKLIST.md
contributor_docs:
- AGENTS.md
- CONTRIBUTING.md
- docs/METHOD.md
- docs/topics/contributing/triage.md

validation:
docs_check: cargo xtask docs-check
prep: cargo xtask release-prep-guard --version {version}
preflight: cargo xtask preflight
rust_advisory_audit: cargo audit
release_check: cargo xtask release-check
package: cargo xtask package-crates --version {version}
tagged_guard: cargo xtask release-guard --tag v{version}
legacy_when_needed: cargo xtask legacy-preflight
Comment thread
flyingrobots marked this conversation as resolved.

workflows:
publish: .github/workflows/release-crates.yml
publish_trigger: tag-push
autotag: none

publish:
manual_dispatch_required: false
github_release: true
source_ref: 'v{version}'
registries:
- name: crates.io
packages:
- wesley-core
- wesley-emit-codec
- wesley-emit-rust
- wesley-emit-typescript
- wesley-cli
verify: cargo info {package}@{version}

issue_model:
live_tracker: github
implementation_bucket: 'Goalpost: ... milestone'
release_gate_bucket: 'Release: v{version} milestone'
release_scheduling_axis: 'v{version} label'
triage_axis: 'triage:* label'
required_state_label: 'exactly one of triage:* or v{version}'

release_evidence:
internal_packet: docs/method/releases/v{version}/release.md
verification: docs/method/releases/v{version}/verification.md
user_notes: docs/releases/v{version}.md
121 changes: 1 addition & 120 deletions .github/workflows/cert-shipme.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@ name: SHIPME Certificate

on:
push:
branches: [main, dev, 'feat/*', 'fix/*', 'milestone/*']
paths:
- 'packages/**'
- 'scripts/prepare-shipme-cert-fixture.mjs'
- '.github/**'
- '.github/workflows/cert-shipme.yml'
pull_request:
branches: [main]
paths:
- 'packages/**'
- 'scripts/prepare-shipme-cert-fixture.mjs'
Expand All @@ -21,11 +15,6 @@ permissions:
jobs:
cert:
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
issues: write
pull-requests: write
steps:
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411
Expand Down Expand Up @@ -65,111 +54,3 @@ jobs:
with:
name: SHIPME
path: SHIPME.md

- name: Post SHIPME badge to PR
if: github.event_name == 'pull_request'
id: badge
run: |
echo "BADGE=[SHIPME] PASS · HOLMES fixture · sha ${GITHUB_SHA::7}" >> $GITHUB_OUTPUT

- name: Wait for HOLMES suite comment
if: github.event_name == 'pull_request'
id: holmes_comment_wait
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
with:
script: |
const marker = `<!-- HOLMES_SUITE_SHA:${context.sha} -->`;
const started = Date.now();
const workflowId = 'wesley-holmes.yml';
const timeoutMs = 2 * 60 * 60 * 1000;
const pollIntervalMs = 15 * 1000;
const maxNoRunFound = 20;
let noRunFoundCount = 0;

while (Date.now() - started < timeoutMs) {
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});

const holmesComment = comments.find(
(comment) => comment.user?.login === 'github-actions[bot]' && comment.body?.includes(marker)
);

if (holmesComment) {
core.setOutput('ready', 'true');
return;
}

const workflowRunsResponse = await github.request(
'GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs',
{
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: workflowId,
event: 'pull_request',
head_sha: context.sha,
per_page: 20
}
);

const currentRun = workflowRunsResponse.data.workflow_runs
.sort((left, right) => new Date(right.created_at) - new Date(left.created_at))[0];

if (!currentRun) {
noRunFoundCount += 1;
if (noRunFoundCount > maxNoRunFound) {
core.setOutput('ready', 'false');
core.setFailed(`No wesley-holmes.yml run found for ${context.sha} after ${maxNoRunFound} polls.`);
return;
}

await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
continue;
}

noRunFoundCount = 0;

if (currentRun?.status === 'completed') {
core.setOutput('ready', 'false');
core.setFailed(
`HOLMES suite workflow ${currentRun.id} for ${context.sha} finished with conclusion ${currentRun.conclusion || 'unknown'} before posting the Holmes PR comment.`
);
return;
}

await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
}

core.setOutput('ready', 'false');
core.setFailed(`HOLMES suite comment for ${context.sha} did not appear within the 2 hour coordination window.`);

- name: Create/Update PR Comment
if: github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' && steps.holmes_comment_wait.outputs.ready == 'true'
continue-on-error: true
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
with:
script: |
const badge = process.env.BADGE || '${{ steps.badge.outputs.BADGE }}';
const marker = '<!-- SHIPME_COMMENT -->';
const body = `${marker}\n### 🚢 SHIPME\n\n${badge}\n`;
try {
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});
const botComment = comments.find(
(c) => c.user?.login === 'github-actions[bot]' && c.body?.includes(marker)
);
if (botComment) {
await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: botComment.id, body });
} else {
await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body });
}
} catch (error) {
core.warning(`Could not post SHIPME badge to PR: ${error.message}`);
}
2 changes: 1 addition & 1 deletion .github/workflows/release-crates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ jobs:
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
for crate in wesley-core wesley-emit-rust wesley-emit-typescript wesley-cli; do
for crate in wesley-core wesley-emit-codec wesley-emit-rust wesley-emit-typescript wesley-cli; do
cargo info "${crate}@${version}" >/dev/null
done

Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,20 @@ hash`, and `schema operations`.

### Changed

- **Release lifecycle profile**: Added a repo-local `.continuum/release.yml`
release profile and expanded the Wesley release doctrine/runbook around
thesis, scope, goalposts, immutable tagged-main publication, verification,
and retrospective evidence.
- **Release documentation gate**: The release runbook, release policy, and
human sign-off checklist now require a `docs/topics/` accuracy and coverage
audit before tagging, with minimum 90% accuracy and 90% coverage floors.
- **HOLMES schema selection**: The HOLMES workflow now reads the Wesley project
manifest first, computes changed schema sets with `wesley config
changed-schemas`, runs schema-scoped matrix jobs, and keeps per-schema report
artifacts grouped for one aggregate PR comment.
- **HOLMES distribution direction**: Documented tagged reusable GitHub Actions
workflows plus copy/paste templates as the user-facing HOLMES install path,
with GitHub App delivery deferred to future identity or Checks API needs.
- **Extension documentation**: Added current project-manifest and module
authoring references, and clarified that `wesley.config.mjs` and the dynamic
JavaScript module loader are retired from generic Wesley core.
Expand All @@ -52,6 +59,26 @@ changed-schemas`, runs schema-scoped matrix jobs, and keeps per-schema report

### Fixed

- **Release crate visibility check**: The tag-triggered Release Crates workflow
now verifies crates.io visibility for `wesley-emit-codec` along with the rest
of the published Rust crate set before finalizing the GitHub Release.
- **Release version-source enforcement**: `cargo xtask release-prep-guard`,
`cargo xtask release-guard`, `cargo xtask package-crates`, and
`cargo xtask publish-crates` now reject root `package.json` and unpublished
required Cargo manifest version drift in addition to published Rust crate
manifest drift.
- **Release milestone gate enforcement**: Release guards now query the declared
`Release: vX.Y.Z` milestone bucket so release-gate issues cannot survive
tagging merely because they lack a version label or exact title/body token.
- **Release advisory-audit profile**: The repo-local release profile now
declares the Rust advisory audit command alongside the other release
validation gates.
- **Release signpost profile coverage**: The repo-local release profile now
includes the public MkDocs source and guide page in user-doc signposts so
profile-driven audits cover public release wording.
- **Post-merge SHIPME certification**: `cert-shipme.yml` now runs only on
`main` pushes, so SHIPME certificates bind to the landed target-branch SHA
instead of racing PR-time HOLMES comments for a temporary merge SHA.
- **Docs CLI checker determinism**: The docs command checker now reads the
native command list from the Rust CLI source help text instead of invoking
`cargo run`, so Node-only repository hygiene does not depend on Cargo
Expand Down
7 changes: 6 additions & 1 deletion docs/CRATES_IO_RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# CRATES.IO RELEASE

<!-- docs-truth: status=experimental owner=@flyingrobots -->
<!-- docs-truth: status=current owner=@flyingrobots -->

This is Wesley's official Rust release procedure.

The distribution target is crates.io. The release authority is GitHub Actions.
Humans prepare commits and tags; GitHub Actions performs the publish.

The repo-local release profile is [`.continuum/release.yml`](../.continuum/release.yml).
It declares the publish crate set, version sources, signposts, and verification
commands that this procedure must match.

## Non-Negotiable Policy

1. Releases must only be performed by GitHub Actions.
Expand Down Expand Up @@ -86,6 +90,7 @@ The `release-gauntlet` job must verify:
- tag commit is reachable from `origin/main`
- every published `Cargo.toml` version matches the tag
- every internal Wesley dependency version matches the tag
- root `package.json` version matches the tag
- every publishable crate has the minimum package file set
- root `README.md` exists
- root `CHANGELOG.md` contains release notes for the exact version
Expand Down
14 changes: 14 additions & 0 deletions docs/architecture/holmes-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ Manifest `commentMode` controls the final PR comment behavior:
| `append` | Create a new PR comment for each run. |
| `silent` | Run analysis but do not write a PR comment. |

## Distribution Boundary

HOLMES should be packaged for external users as a tagged reusable workflow with
documented workflow templates. That keeps execution inside the consumer
repository's GitHub Actions environment, where checkout state, permissions,
artifacts, and logs are inspectable by the repository owner.

A GitHub App is not the first-class delivery mechanism. It remains deferred
unless HOLMES needs a durable certifying identity, Checks API ownership,
cross-repo dashboards, or organization-level policy orchestration. If such an
app exists later, it should consume evidence produced by repository-local
Actions runs rather than becoming a hidden compiler or source of target
semantics.

## Dashboard Artifact

The workflow uploads `docs/holmes-dashboard` as a dashboard template and
Expand Down
8 changes: 7 additions & 1 deletion docs/governance/RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Automated checks are not listed here — those run inside
creating a tag, run `cargo xtask release-check` locally; it runs the same
strict preflight gate used by `release-guard`, then builds and packages the
native release artifacts without publishing anything. This checklist covers
checks 7, 10, 13, 18, 22, and 23 from the enforcement matrix, which require
checks 7, 10, 13, 18, 22, 23, and 24 from the enforcement matrix, which require
human judgment.

See [`RELEASE_POLICY.md`](RELEASE_POLICY.md) for the full enforcement matrix
Expand Down Expand Up @@ -48,6 +48,12 @@ and rationale.
workflows are met. Any stale topic claim, obsolete instruction, missing
topic, or missing authoritative link was corrected before tagging.

- [ ] **Release thesis and scope are honest**
I confirmed the release packet records the thesis, must-ship work,
may-slip work, explicitly-not-included work, selected goalposts, acceptance
evidence, and retrospective/evidence location. Any planned work that did
not ship was moved, cut, or acknowledged before tagging.

- [ ] **No known issues being silently shipped**
I reviewed the open GitHub Issues for known defects or outstanding decisions
that affect this release's correctness or safety, whether or not they are
Expand Down
Loading