---
name: pa-brat-beta-release
description: Manage Personal Assistant BRAT beta prerelease workflow. Use when the user asks to prepare, explain, validate, publish, or follow up a BRAT beta/prerelease build; asks about beta branch management; wants to move master-integrated work into BRAT testing; or needs the work branch to master to beta packaging or stable release process.
---

# PA BRAT Beta Release

Use this skill for Personal Assistant prerelease builds intended for BRAT beta
testers. The detailed repo SOP is `docs/operations/brat-beta-testing.md`; read it before
changing the workflow or executing a beta release.

## Branch Model

Keep these roles distinct:

- `master`: the sole integration and release-source branch. All accepted runtime
  code, tests, research/design docs, governance and release-tooling changes land
  here through a PR merge or an explicitly authorized direct commit.
- Work branch: optional isolation/review transport. It has no beta or stable
  release authority; accepted commits must enter `master` first.
- Beta packaging branch: temporary branch named exactly `beta/<target-version>`,
  created from the exact verified `master` HEAD.

A beta branch may contain only the generated `[release] vX.Y.Z-beta.N` packaging
commit and tag above `master`. Do not add feature/fix/docs commits there, and do
not merge or rebase the beta release commit back to `master`.

## Safety Boundaries

- Treat `make release`, `make publish`, tag creation, branch pushes, GitHub
  Releases, and sending BRAT tester instructions to others as release-side effects.
- Do not publish, push branches, push tags, create GitHub Releases, or hand off
  BRAT tester instructions/URLs to others unless the user clearly asks for that
  action in the current turn. Reporting a verified published URL to the user in
  this conversation is read-only and does not require separate authorization.
- If the target version, `master` baseline, or baseline tag is ambiguous, stop and
  ask before creating release state.
- Prefer `make release-dry-run VERSION=x.y.z-beta.N` before any local release
  commit/tag.

## Preparation Workflow

Choose the lane from the user's request:

- **Explain/status/inspect:** read local state, the runbook, and remote status
  when needed. Do not fetch, switch, pull, create branches, or write release state.
- **Dry run only:** inspect `scripts/release.mjs` and run the existing dry-run
  command when its prerequisites already hold. It writes no release files, but
  currently requires a clean matching `beta/<version>` branch at `master` HEAD
  and a tagged baseline. Report unmet prerequisites; a dry-run request alone
  does not authorize creating branches or changing the checkout to satisfy them.
- **Prepare:** perform the workflow below within the requested scope. Preparing
  the baseline/packaging branch does not authorize a release commit, tag, or push.

When asked to prepare a beta:

1. Inspect current state:
   - `git status --short --branch`
   - `git branch --show-current`
   - `node -p "require('./package.json').version"`
   - `git tag --sort=-v:refname | sed -n '1,20p'`
   If `git status --short --branch` shows any uncommitted changes, stop before
   switching or creating beta branches. Ask the user to commit, stash, clean, or
   explicitly confirm the intended dirty-worktree scope.
2. Confirm all accepted work is already in `master`. A work branch with commits
   not reachable from `master` must be merged by PR or authorized direct commit
   before beta preparation continues.
3. Refresh and verify the local integration baseline:
   - `git fetch origin master`
   - `git switch master`
   - `git pull --ff-only`
   - `git rev-list --left-right --count master...origin/master`
   Require both counts to be zero before creating the packaging branch. This
   one preparation check is necessary: `make release` treats a live master
   mismatch as unavailable CI evidence and falls back to local checks; only
   `make publish` rejects it. A successful `git pull --ff-only` alone does not
   exclude local commits ahead of origin.
   - do not run another full gate here: `make release` obtains exact-master CI
     evidence or runs the full local fallback after the packaging branch is ready
   If local `master` is ahead, beta preparation must stop until the user
   explicitly authorizes pushing `master` and the two refs match.
   Rely on release/publish scripts for their remaining source/ref/version
   checks instead of repeating them manually.
4. Choose the next prerelease version, usually `<next-stable>-beta.N`.
5. Create the packaging branch from the exact current `master` HEAD:
   - `git switch -c beta/<target-version>`
6. Run or recommend:
   - `make release-dry-run VERSION=<target-version>`
   - `make release VERSION=<target-version>` only when the user asked to create
     local release state.
   - `make publish VERSION=<target-version>` only when the user asked to publish
     and the publish preflight below passes.

The release command uses the release-critical documentation gate. Full
`docs:check` lifecycle/status findings remain a separate CI and maintenance
signal and must not block beta or stable publication.

## Validation Reuse And Cost

Use ordinary `make release VERSION=<beta>` once. It automatically tries to
reuse the latest same-repository master push CI for the exact clean,
synchronized source SHA. The current attempt must have passed the full
`validate` job, including dependencies, Lint, Build, Test and Audit bundle;
docs-only success, skipped steps, old SHA/run/attempt or incomplete API data
cannot substitute. The script prints the accepted run URL/SHA or fallback reason.

Reuse retains local diff, third-party notice and release-doc checks. Missing
evidence, unsupported origin, missing `gh` or a bounded API timeout falls back
to the existing local full gate. `RELEASE_LOCAL_CHECKS=1 make release VERSION=...`
forces full local checks for diagnosis. Stable uses the same evidence rules;
dry-run does not query CI or execute checks. Do not use `SKIP_CHECKS` as a
substitute for this evidence check.

Beta publication follows completed functionality acceptance on master. For
normal generated packaging, tag CI independently reuses successful full master
push CI for the exact release parent, with the same repository/current attempt
and required full steps above. Normal master advances are allowed while that
parent remains in master history. It installs dependencies, builds versioned
assets, runs artifact tests, and retains metadata, notice, release-doc, bundle
audit and asset checks; it does not repeat source lint/full Jest/coverage.
Missing, invalid, failed, docs-only, incomplete or unavailable evidence falls
back to the full gate; invalid source/packaging identity rejects publication.
Stable also uses exact parent CI reuse under the release runbook. This replaces
the earlier always-full beta tag rule.

Reused CI does not prove this machine's node_modules or old dist is valid for
deployment. Do not add another test/build before or after `make release`, or
while waiting on tag CI, without changed inputs or a concrete failure. Normal
beta packaging does not require redeployment or repeated functionality smoke.

`scripts/release.mjs` enforces both the matching `beta/<target-version>` name and
the pre-release `HEAD == master` source invariant.

## Publish Preflight

Use `make publish VERSION=<target-version>` after accepted scope and validation.
Trust its clean-worktree, branch, tag/HEAD, source-parent, version and packaging
checks instead of manually repeating each SHA/ref/version query.
`scripts/publish-release.mjs` queries live `origin/master`, then pushes the beta
branch + tag atomically. If `master` advances normally
after the live preflight, the workflow accepts the verified source parent as an
ancestor; divergent/rewritten master history is rejected.

Reuse a normal successful push receipt. Recheck remote refs only for an
ambiguous result, concurrent change, or a next action that needs current remote
state. Script/workflow live preflight remains required.

## Publish Verification

After publish, verify the GitHub prerelease before claiming BRAT readiness:

```bash
gh release view <target-version> \
  --json tagName,name,isDraft,isPrerelease,assets \
  --jq '{tagName,name,isDraft,isPrerelease,assets:[.assets[].name]}'
```

Expected:

- `tagName` and `name` equal `<target-version>`.
- `isPrerelease` is `true`.
- `isDraft` is `false` and the tag workflow completed successfully.
- Assets include `main.js`, `manifest.json`, `styles.css`, `LICENSE`, `NOTICE`,
  and `THIRD_PARTY_NOTICES.md`.
- The released `manifest.json` asset has `version` equal to `<target-version>`.

Download only `manifest.json` for the default completion check. Download all
assets for local hashes/JS syntax checks only when explicitly requested or a
specific artifact/download failure needs diagnosis. Wait for download completion
before inspecting the file. Asset verification is not BRAT/app/device smoke.

Use workflow-step changes and blockers for progress updates. If the host
requires periodic updates during an unchanged step, keep them short; do not
launch redundant checks to fill the wait. Separate local preparation, remote
tag gate and post-publish timing; polling/sleep overlaps the running gate and
must not be added to its elapsed time.

For release workflow failures, inspect GitHub Actions before giving testers the
BRAT URL.

## BRAT Smoke

Do not claim BRAT validation unless the plugin was installed or updated through
BRAT from the published GitHub Release.

Normal packaging beta reuses completed master functionality acceptance and
verifies the Release/assets. Do not automatically deploy or repeat Obsidian,
BRAT Chat/Memory/Pagelet, or mobile smoke.

Trigger targeted install/app/device smoke only for installation or asset
layout, plugin ID or platform changes; a concrete download/load/upgrade failure;
or an explicit request. Choose BRAT install/update and enable/reload/Settings
for installation changes, the affected action for a load/runtime issue, and
mobile only for the affected platform or request. New runtime fixes return to
master functionality acceptance before packaging.

For app smoke, use `obsidian-test-vault-smoke`; for iOS, use
`obsidian-ios-real-device-smoke`.

## Stable Graduation

When beta blockers are closed:

1. Confirm every accepted beta fix is already on `master`; fixes may enter by PR
   or authorized direct commit, never only on a beta branch.
2. Verify `master` again. Do not merge beta release commits or prerelease
   metadata into it.
3. Cut the stable release directly from `master`:
   - `git switch master`
   - `git pull --ff-only`
   - `git branch --show-current`
   - `git status --short`
   - `make release-dry-run VERSION=<stable-version>`
   - `make release VERSION=<stable-version>`
   - `make publish VERSION=<stable-version>` only after explicit publish intent.

Stable changelog generation ignores prerelease tags, so the stable release notes
should cover the full range from the previous stable tag.

## Recovery

- If a beta is bad, put the fix on `master` first. If no published tag exists,
  recreate the packaging branch from that updated `master` only with explicit
  authority to replace local release state.
- If a beta is already published, publish the next beta tag such as
  `2.9.0-beta.3` from updated `master`; do not rewrite tags without explicit
  maintainer approval.
- If `make release-dry-run` reports the current package version is untagged,
  stop and resolve the baseline tag before proceeding.
