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
13 changes: 13 additions & 0 deletions .aiox-core/development/agents/devops.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,19 @@ Type `*help` to see all commands.
- ❌ Creating PR before quality gates pass
- ❌ Skipping CodeRabbit CRITICAL issues

### Release Procedure (NON-NEGOTIABLE Reference)

When invoked with `*release`, `*push` followed by version-bump intent, or any task that ends with a tag push to `@aiox-squads/*`, **load and follow `docs/guides/release-procedure.md` as the canonical SOP before touching anything**. It is the authoritative playbook — the task templates `publish-npm.md` and `release-management.md` are thin wrappers around it.

The SOP captures lessons paid for in 11 patches across 30 days:
- Two-system branch protection on `main` (modern ruleset id `13330052` + legacy `required_pull_request_reviews`); `gh pr merge --admin` bypasses neither alone — you must relax both and restore both atomically with `trap EXIT` + sanitized payloads (raw GitHub API responses include read-only fields that PUT rejects).
- 4-site version bump coordination (`package.json`, `compat/aiox-core/package.json` + its dep, `packages/installer/package.json`, `package-lock.json` + `CHANGELOG.md`).
- The `publish_legacy_aiox_core` job depends on `publish` completing (compat wrapper transitively depends on the scoped package — race against npm CDN propagation has bitten us).
- npm propagation budget for the legacy smoke (240s with dual visibility check).
- Windows path escape in workflow `node -e` interpolations of `${{ github.workspace }}` (use env vars).

Skipping the SOP because "it's just a patch release" is how the next 30-day patch storm starts.

### Related Agents

- **@dev (Dex)** - Delegates push operations to me
Expand Down
254 changes: 17 additions & 237 deletions .aiox-core/development/tasks/publish-npm.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,247 +11,27 @@ checklists:
- release-checklist.md
---

# npm Publishing Pipeline: Preview to Latest
# npm Publishing Pipeline

## Purpose
> **AUTHORITATIVE SOURCE:** [`docs/guides/release-procedure.md`](../../../docs/guides/release-procedure.md)
>
> This task wraps the canonical release SOP. **Open it first**, follow its checklist top-to-bottom. The SOP captures pre-flight checks, 4-site version bump coordination, branch protection bypass (two systems — modern ruleset + legacy branch protection — both must be relaxed and restored atomically via `trap EXIT`), tag-driven publish trigger, post-publish verification, known CI quirks (legacy publish race, npm propagation timeout, Windows path escape), and rollback steps.
>
> This shorter task remains because some agent workflows still reference the `publish-npm` id, but the content of record lives in the SOP. **Do not duplicate the procedure here** — that creates drift between two sources of truth.

Safe, validated npm publishing using a two-phase release strategy:
1. **Preview**: Publish to `preview` dist-tag for testing
2. **Promote**: After validation, promote `preview` to `latest`
## When this task fires

This prevents broken releases reaching users (like v4.0.0-v4.0.4 incident).
- `@devops` is asked to publish a new release of `@aiox-squads/core` (or any of the companion `@aiox-squads/*` packages)
- A release follow-up is needed (workspace package out-of-band publish, hotfix, etc.)

## Commands
## TL;DR (full detail in the SOP)

### `*publish-preview` - Publish as Preview
1. **Pre-flight:** lint, full test suite, `gh secret list` for `NPM_TOKEN_AIOX_SQUADS`, working tree clean, local main synced with origin.
2. **Bump version in 4 places:** `package.json`, `compat/aiox-core/package.json` (both `version` and the `@aiox-squads/core` dep), `packages/installer/package.json`, then refresh `package-lock.json` via `npm install --package-lock-only`. Add a `CHANGELOG.md` entry.
3. **Branch + PR + bypass + merge:** `chore/release-X.Y.Z` → PR to main → atomic bypass→merge→restore (the SOP's `trap EXIT` block is mandatory; do not skip).
4. **Tag + push:** `git tag -a -m "<notes>" vX.Y.Z origin/main` then `git push origin vX.Y.Z`. (Options before tag name, `origin/main` is the ref being tagged.) The tag triggers `.github/workflows/npm-publish.yml`.
5. **Post-publish:** verify `npm view @aiox-squads/core version` returns the new version on `dist-tags.latest`; pull the published tarball with `npm pack @aiox-squads/core@X.Y.Z` and grep for the fix to confirm code presence; run an E2E in the worst-case install topology if the release fixes installer behavior.

Publishes a new version to npm under the `preview` dist-tag.
## Historical context

#### Workflow

```
1. PRE-FLIGHT CHECKS
- Verify: branch = main, working tree clean
- Verify: git is up-to-date with remote (git fetch + compare)

2. QUALITY GATES
- npm run lint
- npm run typecheck
- npm test

3. PACKAGE VALIDATION
- npm run validate:package
(runs scripts/validate-package-completeness.js)
- Confirms: hooks, rules, bin, core config present
- Confirms: pro/ NOT in tarball

4. VERSION BUMP
- Ask user: patch | minor | major
- npm version {type} --no-git-tag-version
- git add package.json package-lock.json
- git commit -m "chore(release): bump version to {new_version}"

5. PUBLISH
- npm publish --tag preview
- Verify: npm view aiox-core@preview version === {new_version}

6. SMOKE TEST
- Create temp directory
- npm init -y
- npm install aiox-core@preview
- Verify critical files exist in node_modules/aiox-core/:
- .claude/hooks/synapse-engine.cjs
- .aiox-core/core-config.yaml
- bin/aiox.js
- Clean up temp directory

7. PUSH VERSION COMMIT
- git push origin main

8. REPORT
- "v{X.Y.Z} published as preview"
- "Test with: npm install aiox-core@preview"
- "When ready: *promote-latest"
```

#### Pre-conditions

- [ ] On `main` branch
- [ ] Working tree clean (no uncommitted changes)
- [ ] All quality gates pass (lint, typecheck, test)
- [ ] Package validation passes (validate-package-completeness.js)
- [ ] npm auth configured (`npm whoami` succeeds)

#### Post-conditions

- [ ] New version published to npm with `preview` tag
- [ ] `npm view aiox-core@preview` returns new version
- [ ] Smoke test passes (critical files present in installed package)
- [ ] Version bump committed and pushed to main

---

### `*promote-latest` - Promote Preview to Latest

Promotes a tested `preview` version to the `latest` dist-tag.

#### Workflow

```
1. VERIFY PREVIEW EXISTS
- npm view aiox-core@preview version
- If no preview: HALT with "No preview version found"

2. CONFIRM WITH USER
- Display: "Promote v{X.Y.Z} from preview to latest?"
- Show current latest: npm view aiox-core@latest version
- Require explicit confirmation

3. PROMOTE
- npm dist-tag add aiox-core@{version} latest

4. VERIFY
- npm view aiox-core@latest version === {version}
- If mismatch: HALT with error

5. TAG & RELEASE
- git tag v{version}
- git push origin v{version}
- gh release create v{version} --generate-notes --latest

6. REPORT
- "v{X.Y.Z} promoted to latest"
- Release URL
- "Install with: npm install aiox-core"
```

#### Pre-conditions

- [ ] Preview version exists (`npm view aiox-core@preview`)
- [ ] User has tested the preview version
- [ ] npm auth configured
- [ ] GitHub CLI authenticated (`gh auth status`)

#### Post-conditions

- [ ] `npm view aiox-core@latest` returns promoted version
- [ ] Git tag `v{version}` created and pushed
- [ ] GitHub Release created with auto-generated notes

---

### `*test-install` - Test Installation in Clean Environment

Tests package installation from a specific dist-tag in a clean temporary directory.

#### Workflow

```
1. SETUP
- Create temporary directory
- npm init -y

2. INSTALL
- npm install aiox-core@{tag} (default: latest)
- Record: install time, exit code, warnings

3. VERIFY FILES
- Check node_modules/aiox-core/ contains:
- .aiox-core/core-config.yaml
- .aiox-core/constitution.md
- .aiox-core/development/agents/ (non-empty)
- .aiox-core/development/tasks/ (non-empty)
- .claude/hooks/synapse-engine.cjs
- .claude/hooks/precompact-session-digest.cjs
- .claude/rules/ (non-empty)
- bin/aiox.js
- bin/aiox-minimal.js
- Check node_modules/aiox-core/ does NOT contain:
- pro/
- .env
- .git/
- tests/

4. TEST INSTALLER (optional, if --full flag)
- npx aiox-core install --preset minimal
- Verify: .aiox-core/ created
- Verify: .claude/hooks/ created
- Verify: .claude/rules/ created

5. CLEANUP
- Remove temporary directory

6. REPORT
- Package version installed
- All critical files: present/missing
- Excluded content: clean/leaked
- Overall: PASS/FAIL
```

#### Parameters

- `tag`: dist-tag to test (default: `latest`)
- `--full`: Also test `npx aiox-core install`

---

## Rollback Procedure

If a broken version reaches `latest`:

```bash
# 1. Identify previous good version
npm view aiox-core versions --json

# 2. Point latest back to previous version
npm dist-tag add aiox-core@{previous-good-version} latest

# 3. Deprecate broken version with message
npm deprecate aiox-core@{broken-version} "Known issues, use v{previous-good-version}"

# 4. Verify
npm view aiox-core@latest version
```

**Important:** `npm deprecate` shows a warning on install but does NOT prevent installation.
To fully block a version, use `npm unpublish aiox-core@{version}` (within 72h of publish only).

---

## Configuration Reference

See `core-config.yaml` section `npm_registry` for:
- Required files list
- Excluded paths list
- Smoke test configuration
- Auth strategy

---

## Error Handling

| Error | Cause | Resolution |
|-------|-------|------------|
| `npm ERR! 403` | Auth issue | Run `npm login`, check token permissions |
| `npm ERR! 402` | Paid feature | Ensure package is public (`--access public`) |
| Smoke test fails | Files missing from tarball | Fix `files` array in package.json, re-validate |
| promote fails | Version not on preview | Run `*publish-preview` first |
| Tag already exists | Re-publishing same version | Bump version or use `--force` (with caution) |

---

## Metadata

```yaml
story: INS-2 (Release Pipeline: Preview to Latest)
version: 1.0.0
dependencies:
- release-management.md
- github-devops-pre-push-quality-gate.md
tags:
- npm
- release
- publishing
- preview
created: 2026-02-13
```
The prior version of this file (pre-2026-05-17) contained an inline preview-to-latest two-phase pipeline that worked for early releases but did not document the branch-protection dance, the publish race conditions, or the cross-package version coordination. After three regressions in a single 30-day window, the canonical procedure was consolidated into `docs/guides/release-procedure.md` so every release goes through one validated checklist.
Loading
Loading