diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 946272f7..c39fffa6 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,6 @@ "plugins": [ { "name": "aidd-context", - "version": "1.0.0", "source": "./plugins/aidd-context", "description": "Knowledge production: project bootstrap, project init, generation of context artifacts (skills, agents, rules, commands, hooks), mermaid diagrams, learn, discovery", "strict": true, @@ -17,7 +16,6 @@ }, { "name": "aidd-dev", - "version": "1.0.0", "source": "./plugins/aidd-dev", "description": "Code transformation: Dev SDLC orchestrator (code-shipping pipeline), plan, assert, audit, review, test, refactor, debug, for-sure. Hosts engineering agents.", "strict": true, @@ -25,7 +23,6 @@ }, { "name": "aidd-vcs", - "version": "1.0.0", "source": "./plugins/aidd-vcs", "description": "External artifacts: commit, pull-request, release-tag, issue-create", "strict": true, @@ -33,7 +30,6 @@ }, { "name": "aidd-pm", - "version": "1.0.0", "source": "./plugins/aidd-pm", "description": "Product management: ticket-info, user-stories-create, prd, spec", "strict": true, @@ -41,7 +37,6 @@ }, { "name": "aidd-orchestrator", - "version": "1.0.0", "source": "./plugins/aidd-orchestrator", "description": "Async development orchestration: turn ready issues into pull requests, then iterate on review feedback until a human takes over.", "strict": true, @@ -49,7 +44,6 @@ }, { "name": "aidd-refine", - "version": "1.0.0", "source": "./plugins/aidd-refine", "description": "Meta-cognition: refine input through brainstorming, refine output through challenge and condensed communication mode.", "strict": true, diff --git a/.github/rulesets/next.json b/.github/rulesets/next.json new file mode 100644 index 00000000..74a98815 --- /dev/null +++ b/.github/rulesets/next.json @@ -0,0 +1,39 @@ +{ + "name": "next protection", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + {"actor_id": 3959336, "actor_type": "Integration", "bypass_mode": "always"}, + {"actor_id": 11783938, "actor_type": "Team", "bypass_mode": "pull_request"} + ], + "conditions": { + "ref_name": { + "include": ["refs/heads/next"], + "exclude": [] + } + }, + "rules": [ + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 0, + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_review_thread_resolution": false + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": false, + "required_status_checks": [ + {"context": "lefthook (framework-local checks)"}, + {"context": "Commitlint"} + ] + } + }, + {"type": "non_fast_forward"}, + {"type": "deletion"} + ] +} diff --git a/.github/workflows/back-merge.yml b/.github/workflows/back-merge.yml new file mode 100644 index 00000000..2a234956 --- /dev/null +++ b/.github/workflows/back-merge.yml @@ -0,0 +1,52 @@ +name: Back-merge + +# After a release on main, sync main back into next so its changelog, manifest, +# and version bumps do not drift. No conflict -> push next directly (the App is +# an "always" bypass actor on next). Conflict -> open a PR for a human. + +on: + release: + types: [published] + +concurrency: + group: back-merge + cancel-in-progress: false + +permissions: {} + +jobs: + back-merge: + name: Back-merge main into next + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + id: app-token + with: + app-id: ${{ secrets.AIDD_BOT_APP_ID }} + private-key: ${{ secrets.AIDD_BOT_PRIVATE_KEY }} + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: next + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Merge main into next + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + git config user.name "aidd-bot[bot]" + git config user.email "aidd-bot[bot]@users.noreply.github.com" + git fetch origin main + if git merge --no-edit origin/main; then + git push origin next + else + git merge --abort + BRANCH="back-merge/main-to-next-${{ github.run_id }}" + git checkout -b "$BRANCH" origin/main + git push origin "$BRANCH" + gh pr create --base next --head "$BRANCH" \ + --title "chore: back-merge main into next (conflicts)" \ + --body "Automated back-merge hit conflicts (CHANGELOG / manifest / version files). Resolve manually, then merge into next." \ + --repo "${{ github.repository }}" + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfbfec7b..3334d65f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,6 +74,20 @@ jobs: config-file: release-please-config.json manifest-file: .release-please-manifest.json + # Auto-merge the Release PR release-please just opened/updated, with the + # App token. `--admin` is required: a plain `gh pr merge` is refused by the + # branch policy ("base branch policy prohibits the merge") even for a + # bypass actor (verified in a sandbox). `--admin` performs the override + # merge that the ruleset's bypass_actors entry permits for this App. + # An App-token merge also re-fires the `push: main` and `release: published` + # workflows that a GITHUB_TOKEN merge would not. + # Guard on prs_created (PR opened), not releases_created (fires on merge). + - name: Auto-merge the Release PR + if: ${{ steps.release.outputs.prs_created == 'true' }} + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: gh pr merge "${{ fromJSON(steps.release.outputs.pr).number }}" --squash --admin --repo "${{ github.repository }}" + build-and-attach: name: Build and attach marketplace needs: [release-please] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ee4e5ea..1fb74f33 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,24 +64,11 @@ Format: `(): description`. git commit -m "feat(aidd-dev): add for-sure skill" ``` -**Scope** - one per commit (split cross-plugin changes): - -| Scope | Path | -| ----- | ---- | -| `aidd-context` / `aidd-dev` / `aidd-vcs` / `aidd-pm` / `aidd-orchestrator` / `aidd-refine` | the matching `plugins//` | -| `marketplace` | `.claude-plugin/marketplace.json` | -| `framework` | root: scripts, CI, configs, docs, `aidd_docs/` | - -**Type** - drives the release: - -- `feat` → minor · `fix` / `perf` → patch · `!` or `BREAKING CHANGE:` → major -- `docs` / `refactor` / `style` / `test` / `build` / `ci` / `chore` → no release - -Versioning and the release bundles are automated - see [Releases](#releases). +One scope per commit (split cross-plugin changes). The types, the scopes, and the rules live in [`aidd_docs/memory/vcs.md`](aidd_docs/memory/vcs.md#commit-convention) - it mirrors `commitlint.config.cjs`, the source of truth. **Type** drives the release; see [`RELEASE.md`](./RELEASE.md) for what each type produces. ## 3. Open a pull request -- Work on a branch, not `main`. +- Branch off `next` and target `next` (the integration branch); `hotfix/*` branches off `main` for urgent production fixes. See [`RELEASE.md`](./RELEASE.md). - **Fill the PR template** (applied automatically): explain *what* changed and *how* you resolved it technically - that narrative is the point of the PR. The conventional title and pre-commit hooks are already enforced by CI, so don't spend the description re-asserting them. - **Label the PR** so reviewers and the [Roadmap board](https://github.com/orgs/ai-driven-dev/projects/8) triage at a glance: @@ -97,16 +84,14 @@ Versioning and the release bundles are automated - see [Releases](#releases). ## Releases -Automated by [release-please](https://github.com/googleapis/release-please) in manifest mode. The repo ships **7 independently-versioned packages** (root `aidd-framework` + the 6 plugins); each bumps from the conventional commits touching its path. +How releases flow (the `main`/`next` model, weekly cadence, hotfix, auto-merge) is in [`RELEASE.md`](./RELEASE.md); the release tooling is in [`aidd_docs/memory/vcs.md`](aidd_docs/memory/vcs.md). What a release produces, for contributors: -- Every push to `main` opens / updates a `chore: release main` PR (changelog + version bumps). -- Merging it tags each bumped package and creates the GitHub Releases; CI then attaches the bundles: +- **7 independently-versioned packages** (root `aidd-framework` + the 6 plugins). +- On release, CI attaches the bundles: - `aidd-framework-marketplace-X.Y.Z.zip` - the Claude Code marketplace (`.claude-plugin/` + `plugins/`); kept as the legacy Claude alias of `aidd-framework-claude-marketplace-X.Y.Z.zip`. - `-vX.Y.Z.zip` - per released plugin. - `aidd-framework---X.Y.Z.zip` - **per-tool distributions** built by `aidd-cli` (`framework build`) on the root release: 4 marketplace (claude/cursor/copilot/codex) + 5 flat (+opencode, flat-only) = 9 archives. Produced by the `build-per-tool` matrix job in `.github/workflows/ci.yml`, pinned to a specific `@ai-driven-dev/cli` version. -Config: `release-please-config.json` + `.release-please-manifest.json` (pre-releases, forced versions, and recovery are driven through those files). - ## Reporting issues [Open an issue](https://github.com/ai-driven-dev/framework/issues/new/choose) (🐛 Bug or ✨ Feature). New issues are auto-added to the [AIDD Roadmap board](https://github.com/orgs/ai-driven-dev/projects/8). For **usage questions**, use [Discussions](https://github.com/ai-driven-dev/framework/discussions), not issues (see [`SUPPORT.md`](./.github/SUPPORT.md)). diff --git a/GOVERNANCE.md b/GOVERNANCE.md index d0e0fe17..066d6b5e 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -76,13 +76,19 @@ none object. - **Conflict of interest**: a Habilité with a stake in a PR discloses it and is not the sole approver (a second Habilité approval becomes mandatory). -## Branch protection on `main` - -No direct push, no force-push, no deletion; every change is a PR with ≥1 Habilité -(CODEOWNERS) approval, passing checks (`lefthook (framework-local checks)`, -`Commitlint`), and resolved threads. Machine-readable form: -[`.github/rulesets/main.json`](.github/rulesets/main.json) (enforced once the repo -is public / on a paid plan). +## Branch protection on `main` and `next` + +`main` is production: no direct push, no force-push, no deletion; every change is +a PR with ≥1 Habilité (CODEOWNERS) approval, passing checks (`lefthook +(framework-local checks)`, `Commitlint`), and resolved threads. Machine-readable +form: [`.github/rulesets/main.json`](.github/rulesets/main.json) (enforced once +the repo is public / on a paid plan). + +`next` is the integration branch: PRs and passing checks, no direct push or +deletion, but no required review so the week's work batches quickly. The release +bot bypasses to push the automated back-merge. Machine-readable form: +[`.github/rulesets/next.json`](.github/rulesets/next.json). The release flow is in +[`RELEASE.md`](RELEASE.md). ## Code of Conduct & amendments diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..6b56092c --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,74 @@ +# Release + +How the AI-Driven Dev Framework ships, and what to follow when you open a +change. Weekly rolling releases, with a fast lane for urgent fixes. + +For branch naming, commit format, and the release tooling, see [`aidd_docs/memory/vcs.md`](aidd_docs/memory/vcs.md). + +For who may merge and release, see [`GOVERNANCE.md`](GOVERNANCE.md). + +## Principle + +- `main` is production. The marketplace tracks it, so users only ever get + released, versioned code. +- `next` is integration. The week's work batches here, and ships in one weekly + release. +- A release happens only when there is work, and is cut automatically by + release-please. +- A `hotfix/*` skips `next`: it branches from `main` and ships its own release + out of cycle, for urgent production fixes. + +## Where your change goes + +Almost everything flows through `next` and ships in the weekly release. Only an +urgent production fix takes the fast lane straight to `main`. + +```mermaid +flowchart LR + work["feat/* · fix/* · docs/* · …"] -->|PR| next + next -->|weekly promotion PR| main + main -->|release-please PR, auto-merged| rel["tag + version bump"] + rel -->|back-merge, auto| next + hotfix["hotfix/*"] -->|PR| main +``` + +Two merges reach `main`: the weekly promotion PR from `next`, then the +auto-merged Release PR that release-please raises. + +## What your commit produces + +The commit type drives the changelog section it lands under. + +| Commit type | Changelog section | +| ----------- | ----------------- | +| `feat` | Features | +| `fix` | Bug Fixes | +| `perf` | Performance | +| `refactor` | Refactoring | +| `docs` | Documentation | +| `chore` | Miscellaneous | +| `revert` | Reverts | + +The version bump is release-please's call: `feat` → minor, `fix` → patch, a `!` +suffix or `BREAKING CHANGE` footer → major. The other types ride along in a +release triggered by a `feat` or `fix`. + +## The two rules that keep it safe + +- **The Release PR is auto-merged.** Otherwise `main` briefly holds merged but + unversioned code, and a new user installing in that window picks it up. +- **The `main` → `next` back-merge is automatic.** Otherwise the version + manifest and the changelog on `next` drift from `main`. + +## Weekly release, step by step + +1. Open the promotion PR `next` → `main` once the week's work is ready. +2. release-please opens a Release PR on `main`; it is auto-merged. +3. Tags and version bumps are created; release artifacts are attached. +4. `main` is back-merged into `next` automatically. + +## Hotfix + +1. Branch `hotfix/*` from `main`, fix, PR back to `main`. +2. release-please cuts a dedicated patch release. +3. `main` is back-merged into `next` automatically. diff --git a/aidd_docs/brainstorm/2026_06_19-rolling-weekly-releases.md b/aidd_docs/brainstorm/2026_06_19-rolling-weekly-releases.md new file mode 100644 index 00000000..79b074b7 --- /dev/null +++ b/aidd_docs/brainstorm/2026_06_19-rolling-weekly-releases.md @@ -0,0 +1,48 @@ +# Rolling weekly releases + +> Brainstorm — 2026-06-19. Idée consolidée, niveau intention. Pas de plan ni de code. + +## L'idée + +Shipper le framework chaque semaine, vite et de façon cadencée, tout en gardant une norme de branches qui permet les hotfix — sans qu'un nouvel utilisateur reçoive jamais du code non-versionné. + +Le framework est un monorepo de plugins distribué via une marketplace : les utilisateurs suivent la branche par défaut du repo à son sommet. Aujourd'hui les features arrivent directement sur cette branche, donc un nouvel utilisateur qui installe récupère du code fusionné mais pas encore publié, sous un ancien numéro de version. C'est le défaut à corriger. + +## Faits qui cadrent la décision + +- Le numéro de version d'un plugin sert de clé de cache : sans incrément, un utilisateur déjà installé ne reçoit rien. +- La marketplace suit la branche par défaut à son sommet ; un nouvel utilisateur prend donc l'état courant de cette branche, versionné ou non. +- Pour une marketplace tierce, la mise à jour automatique est désactivée par défaut côté utilisateur. +- La résolution de version prend le numéro porté par le plugin en premier. + +## La forme retenue + +Deux branches : une branche de **production** (celle que suivent les utilisateurs, qui ne doit contenir que du publié) et une branche d'**intégration** où s'accumulent les features de la semaine. + +- Les features et corrections passent par l'intégration. +- Quand c'est prêt, l'intégration est promue vers la production, ce qui déclenche une publication versionnée **fusionnée automatiquement**, puis une remontée **automatique** de la production vers l'intégration pour resynchroniser. +- Un hotfix part de la production, y revient en publication dédiée hors cadence, puis est remonté vers l'intégration. + +Le gestionnaire de versions existant est conservé et reste la source de l'incrément. + +Alternative écartée : faire surveiller au gestionnaire de versions la branche d'intégration puis fast-forward vers la production. Garantit une production mathématiquement pure (zéro fenêtre d'exposition) mais impose un double suivi et une remontée fragile — complexité jugée supérieure au bénéfice. + +## Contraintes non négociables + +- La publication versionnée doit être fusionnée automatiquement. Sinon, entre la promotion et la publication, la production contient des features non versionnées et un nouvel utilisateur les capte. L'auto-fusion réduit cette fenêtre à la durée d'un passage d'intégration continue. +- La remontée production → intégration doit être automatique, sinon les états de version et de journal divergent. + +## Risque résiduel assumé + +Une fenêtre d'exposition de l'ordre d'un passage d'intégration continue subsiste, une fois par semaine, sur une petite base d'utilisateurs. Jugée négligeable au regard de la simplicité gagnée. + +## Assumptions ouvertes (à confirmer à l'étape suivante) + +- **Où tourne le gestionnaire de versions aujourd'hui** : aucun workflow d'automatisation dans le module `framework/` lui-même ; il tourne probablement à la racine du monorepo parent. À vérifier avant toute modification. +- **Nom de la branche d'intégration** : `next` envisagé, renommable. +- **Cohérence des numéros embarqués dans le manifeste de marketplace** : aujourd'hui figés et divergents des numéros réels des plugins. À nettoyer pour une source unique. +- **Cadence stricte vs opportuniste** : publication seulement s'il y a eu du travail dans la semaine. + +## Prochaine étape + +L'idée est mûre pour une **planification technique** : traduire ce modèle de branches en automatisations concrètes (promotion, auto-fusion de la publication, remontée, protections de branches), documenter le flux de contribution, et vérifier d'abord où l'automatisation de version s'exécute réellement. diff --git a/aidd_docs/memory/vcs.md b/aidd_docs/memory/vcs.md index 3d58ab28..4fc34de5 100644 --- a/aidd_docs/memory/vcs.md +++ b/aidd_docs/memory/vcs.md @@ -1,6 +1,7 @@ # Versioning Control System (VCS) Guidelines -- Main Branch: `main` +- Production branch: `main` +- Integration branch: `next` (default target for day-to-day work) - Platform: `github` - CLI: `gh` - Ticketing Tool: GitHub Issues @@ -15,15 +16,15 @@ type/ticket-short-description ### Types -| Prefix | Usage | -| --- | --- | -| `feat/` | New feature | -| `fix/` | Bug fix | -| `docs/` | Documentation only | -| `refactor/` | Code change (no feat/fix) | -| `chore/` | Build, config, deps | -| `test/` | Add/update tests | -| `hotfix/` | Urgent production fix | +| Prefix | Usage | Branch from | PR target | +| --- | --- | --- | --- | +| `feat/` | New feature | `next` | `next` | +| `fix/` | Bug fix | `next` | `next` | +| `docs/` | Documentation only | `next` | `next` | +| `refactor/` | Code change (no feat/fix) | `next` | `next` | +| `chore/` | Build, config, deps | `next` | `next` | +| `test/` | Add/update tests | `next` | `next` | +| `hotfix/` | Urgent production fix | `main` | `main` | ### Examples @@ -34,6 +35,8 @@ docs/update-api-examples chore/release-please-config ``` +`next` is the day-to-day branch: branch from it, target it. `main` is production and only takes promotions from `next` plus `hotfix/*`. The release flow is in [`RELEASE.md`](../../RELEASE.md). + ## Commit Convention ### Source of truth @@ -103,7 +106,11 @@ BREAKING CHANGE: plugin.json now requires a `strict` field. ## Release Management -- Automated via `release-please` (GitHub Actions) -- Config: `release-please-config.json`, manifest: `.release-please-manifest.json` -- Per-plugin versioning with `include-component-in-tag: true` -- Tags format: `-v` (e.g. `aidd-dev-v1.2.0`) +The release flow (main/next model, weekly cadence, hotfix) lives in [`RELEASE.md`](../../RELEASE.md). This section is the tooling only. + +- Automated via `release-please`, in `.github/workflows/ci.yml` on push to `main`. +- The Release PR is auto-merged in that workflow (AIDD bot App token, a bypass actor on `main`). +- Back-merge `main` -> `next` is automated in `.github/workflows/back-merge.yml`. +- Config: `release-please-config.json`, manifest: `.release-please-manifest.json`. +- Per-plugin versioning with `include-component-in-tag: true`. +- Tags format: `-v` (e.g. `aidd-dev-v1.2.0`). diff --git a/aidd_docs/tasks/2026_06/1_branch-model-ci.md b/aidd_docs/tasks/2026_06/1_branch-model-ci.md new file mode 100644 index 00000000..3535a4fd --- /dev/null +++ b/aidd_docs/tasks/2026_06/1_branch-model-ci.md @@ -0,0 +1,80 @@ +--- +name: phase +description: Living implementation plan - frozen objective, phases, and append-only execution Log. Used as input artifact AND as the autonomous-loop tracking file. +status: pending +iteration: 0 +created_at: "2026-06-19T09:12:57Z" +--- + +# Instruction: Branch model + CI automation + +| Element | Value | +| --------------- | --------------- | +| **Plan** | `aidd_docs/tasks/2026_06/2026_06_19-rolling-weekly-releases.md` | +| **Branch name** | `feat/rolling-release-branch-model` | + +## Architecture projection + +```txt +. +├── .github/ +│ ├── workflows/ +│ │ ├── ci.yml # 🔁 add auto-merge step for the release-please PR +│ │ └── back-merge.yml # ✅ auto back-merge main -> next on release published +│ └── rulesets/ +│ ├── main.json +│ └── next.json # ✅ branch protection for next (based on main.json, lighter) +└── (repo) next # ✅ new long-lived integration branch +``` + +## User Journey + +```mermaid +flowchart TD + A[Dev opens PR to next] --> B[Weekly promotion PR next to main] + B --> C[release-please opens Release PR on main] + C --> D[Release PR auto-merged] + D --> E[Tags + version bump + artifacts] + E --> F[back-merge.yml fast-forwards main into next] +``` + +## Tasks to do + +### `1)` Create the `next` branch + +> Long-lived integration branch, cut from `main`. + +1. Branch `next` from current `main`, push to origin. +2. Confirm it tracks `main` history (no divergence at creation). + +### `2)` Add `next` branch protection + +> Protect `next` like `main`, lighter on reviews. + +1. Add `.github/rulesets/next.json` targeting `refs/heads/next`. +2. Require PRs and the same status checks (`lefthook`, `Commitlint`); keep `non_fast_forward` and `deletion`. +3. Keep the AIDD bot App in `bypass_actors` so the back-merge can push. + +### `3)` Auto-merge the release-please PR + +> Close the unversioned-code window on `main`. + +1. In `ci.yml`, after the `release-please` step, add a step that enables auto-merge on the opened release PR (`gh pr merge --auto`) using the App token. +2. Ensure repo setting "Allow auto-merge" is on (note for the maintainer if it is a UI-only toggle). +3. Do not modify `build-and-attach`, `build-per-tool`, `build-plugin`. + +### `4)` Back-merge workflow `main` -> `next` + +> Keep `next` in sync after every release. + +1. Add `.github/workflows/back-merge.yml`, triggered on release published (or push to `main` after a release). +2. Fast-forward / merge `main` into `next` using the App token; fail loudly if it cannot. + +## Test acceptance criteria + +| Task | Acceptance criteria | +| ---- | ------------------------------------ | +| 1 | `git ls-remote --heads origin next` returns a ref. | +| 2 | `.github/rulesets/next.json` exists, targets `refs/heads/next`, and validates as JSON (`jq . next.json`). | +| 3 | `ci.yml` contains a `gh pr merge --auto` step gated on the release-please PR; `yamllint`/`actionlint` passes. | +| 4 | `.github/workflows/back-merge.yml` exists, triggers on release, and `actionlint` passes. | diff --git a/aidd_docs/tasks/2026_06/2026_06_19-rolling-weekly-releases.md b/aidd_docs/tasks/2026_06/2026_06_19-rolling-weekly-releases.md new file mode 100644 index 00000000..407832d2 --- /dev/null +++ b/aidd_docs/tasks/2026_06/2026_06_19-rolling-weekly-releases.md @@ -0,0 +1,70 @@ +--- +name: plan +description: Implementation plan - objective, phases, risks, and decisions, with one phase file per phase alongside it. +objective: "Ship a weekly rolling-release model on main/next with an auto-merged release PR and an automatic main->next back-merge, and align the docs." +success_condition: "git ls-remote --heads origin next succeeds, ci.yml auto-merges the release-please PR, a back-merge workflow exists, and RELEASE.md/vcs.md/GOVERNANCE.md cross-link with no duplicated release flow." +plan_status: in_progress +iteration: 0 +created_at: "2026-06-19T09:12:57Z" +--- + +# Plan: Rolling weekly releases (main/next) + +## Overview + +| Field | Value | +| -------------- | ------------------ | +| **Goal** | Weekly rolling releases on `main`/`next`, auto-merged release PR, auto back-merge, docs aligned with no duplication. | +| **Risk Score** | 4/10 | +| **Source** | `aidd_docs/brainstorm/2026_06_19-rolling-weekly-releases.md` + verified repo state | + +## Applicable rules + +| Tool | Rule | Path | Why it applies | +| ------ | -------- | -------- | -------------- | +| CLAUDE.md | No doc duplication | `framework/CLAUDE.md` | RELEASE.md, vcs.md, GOVERNANCE.md must link to a single home, not copy the flow. | +| CLAUDE.md | Responsibility placement | `docs/ARCHITECTURE.md` | Each fact lives where it belongs: flow vs mechanics vs authority. | +| memory | VCS conventions | `aidd_docs/memory/vcs.md` | Branch and commit conventions are extended, not rewritten. | +| governance | Merge authority and branch protection | `GOVERNANCE.md` | Branch-protection policy is the authority home; `next` rules belong there. | +| plan | English only | plan skill | Plan and docs are written in English. | + +## Phases + +| # | Phase | File | +| --- | --------------------------- | ----------------------------------------- | +| 1 | Branch model + CI automation | [`./1_branch-model-ci.md`](./1_branch-model-ci.md) | +| 2 | Marketplace version cleanup | [`./2_marketplace-version-cleanup.md`](./2_marketplace-version-cleanup.md) | +| 3 | Docs alignment | [`./3_docs-alignment.md`](./3_docs-alignment.md) | + +## Risk register + +| Risk | Impact | Mitigation | +| -------- | ----------------------------- | ------------------------------------- | +| Release PR not actually auto-merging | Leak window stays open; new users get unversioned code | Verify the `gh pr merge --auto` step + repo "Allow auto-merge" + the App bypass on `main` before relying on it. | +| Back-merge missing or failing | `next` manifest/changelog drift from `main`, promotion conflicts | Dedicated back-merge workflow on release published; fail loud if it cannot fast-forward. | +| Docs land ahead of CI | Readers follow a flow `next`/auto-merge that does not exist yet | Phase 1 (branch + automation) ships before Phase 3 surfaces the docs. | +| Removing `version` from marketplace entries changes resolution | Wrong version served to users | Resolution already prefers `plugin.json`; confirm a dry release still bumps correctly. | +| Existing build jobs depend on release-please outputs | Broken artifacts on release | Do not touch `build-and-attach`, `build-per-tool`, `build-plugin`; only add an auto-merge step. | + +## External resources + +| Source | Verified | +| ------ | -------- | +| https://code.claude.com/docs/en/plugin-marketplaces | `version` is a cache key; resolution prefers `plugin.json`; marketplace tracks the default branch HEAD. | +| https://github.com/prometheus/prometheus/blob/main/RELEASE.md | Root `RELEASE.md` is the common (not formal) convention for a release-process doc. | + +## Decisions + +| Decision | Why | +| -------- | --- | +| Single watched branch: release-please stays on `main` | Simpler than dual-target; one manifest; hotfix uses the same path. | +| `next` is the integration branch, default PR target | Keeps `main` releasable-only and the marketplace clean for new users. | +| Release PR auto-merged | Shrinks the unversioned-code window on `main` to one CI run. | +| `main` -> `next` back-merge automated | Prevents manifest/changelog drift and promotion conflicts. | +| Remove `version` from marketplace.json entries | `plugin.json` is the single source; entries were stale. | + +## Confidence + +| Score | ✓ Raises it | ✗ Risks it | +| ------- | ----------- | ---------- | +| 9/10 | Repo state verified (ci.yml, rulesets, marketplace.json); model decided and faits-checked against docs; phases isolated and independently verifiable. | Exact auto-merge wiring (App bypass vs required reviews on `main`) must be confirmed live; back-merge fast-forward depends on discipline. | diff --git a/aidd_docs/tasks/2026_06/2_marketplace-version-cleanup.md b/aidd_docs/tasks/2026_06/2_marketplace-version-cleanup.md new file mode 100644 index 00000000..864716ab --- /dev/null +++ b/aidd_docs/tasks/2026_06/2_marketplace-version-cleanup.md @@ -0,0 +1,46 @@ +--- +name: phase +description: Living implementation plan - frozen objective, phases, and append-only execution Log. Used as input artifact AND as the autonomous-loop tracking file. +status: pending +iteration: 0 +created_at: "2026-06-19T09:12:57Z" +--- + +# Instruction: Marketplace version cleanup + +| Element | Value | +| --------------- | --------------- | +| **Plan** | `aidd_docs/tasks/2026_06/2026_06_19-rolling-weekly-releases.md` | +| **Branch name** | `chore/marketplace-version-single-source` | + +## Architecture projection + +```txt +. +└── .claude-plugin/ + └── marketplace.json # 🔁 remove the stale `version` field from each plugin entry +``` + +## User Journey + +```mermaid +flowchart TD + A[User installs a plugin from the marketplace] --> B[Version resolved from plugin.json] + B --> C[No stale 1.0.0 from the marketplace entry] +``` + +## Tasks to do + +### `1)` Remove `version` from plugin entries + +> `plugin.json` is the single source of version truth. + +1. In `.claude-plugin/marketplace.json`, delete the `version` key from each of the six plugin entries. +2. Keep the top-level marketplace `version` (bumped by release-please via `extra-files`). +3. Leave `release-please-config.json` untouched (it already bumps each `plugin.json` and the marketplace top-level `version`). + +## Test acceptance criteria + +| Task | Acceptance criteria | +| ---- | ------------------------------------ | +| 1 | `jq '.plugins[] | has("version")' .claude-plugin/marketplace.json` returns `false` for every entry, and `jq '.version' .claude-plugin/marketplace.json` still returns the marketplace version. | diff --git a/aidd_docs/tasks/2026_06/3_docs-alignment.md b/aidd_docs/tasks/2026_06/3_docs-alignment.md new file mode 100644 index 00000000..149bcb0a --- /dev/null +++ b/aidd_docs/tasks/2026_06/3_docs-alignment.md @@ -0,0 +1,76 @@ +--- +name: phase +description: Living implementation plan - frozen objective, phases, and append-only execution Log. Used as input artifact AND as the autonomous-loop tracking file. +status: pending +iteration: 0 +created_at: "2026-06-19T09:12:57Z" +--- + +# Instruction: Docs alignment + +| Element | Value | +| --------------- | --------------- | +| **Plan** | `aidd_docs/tasks/2026_06/2026_06_19-rolling-weekly-releases.md` | +| **Branch name** | `docs/release-flow-alignment` | + +## Architecture projection + +```txt +. +├── RELEASE.md # ✅ already written; review only, no rewrite +├── GOVERNANCE.md # 🔁 extend branch protection to next, link RELEASE.md +├── CONTRIBUTING.md # 🔁 default PR target = next +└── aidd_docs/memory/vcs.md # 🔁 next branch, from/target columns, default branch, back-merge pointer, link RELEASE.md +``` + +## User Journey + +```mermaid +flowchart TD + A[Contributor reads RELEASE.md] --> B[Knows where the change goes] + B --> C[Follows vcs.md for branch base + PR target] + C --> D[GOVERNANCE.md says who merges + protections] + D --> E[No duplicated flow across the three] +``` + +## Tasks to do + +### `1)` Review RELEASE.md + +> Already written this session; confirm it stays the single home of the flow. + +1. Confirm principle (incl. hotfix), where-your-change-goes mermaid (two merges to `main`), commit-type -> changelog table, the two rules, weekly + hotfix steps. +2. Keep it succinct; no branch-mechanics table (that is vcs.md), no tooling config (that is vcs.md). + +### `2)` Edit vcs.md + +> Owns the executable mechanics. No flow narrative. + +1. Add `next` as the integration branch and the default working/PR branch. +2. Add `branched from` + `PR target` to the branch-types table: `feat/fix/docs/refactor/chore/test` from+to `next`; `hotfix` from+to `main`. +3. Keep release tooling (release-please in `ci.yml` watching `main`, config/manifest paths, tags `-v`); add a one-line pointer to the automated back-merge. +4. Link to `RELEASE.md` for the flow; do not restate it. + +### `3)` Edit GOVERNANCE.md + +> Authority home for branch protection. + +1. Extend "Branch protection on `main`" to cover `next` (PR + checks, no direct push), referencing `.github/rulesets/next.json`. +2. Link to `RELEASE.md` for the release flow; remove any duplicated flow text. + +### `4)` Edit CONTRIBUTING.md + +> Point contributors at the default target. + +1. State that PRs target `next` by default; `hotfix/*` targets `main`. +2. Link to `RELEASE.md` and `vcs.md` instead of repeating them. + +## Test acceptance criteria + +| Task | Acceptance criteria | +| ---- | ------------------------------------ | +| 1 | `RELEASE.md` contains a `hotfix` principle bullet and a mermaid block; `markdownlint RELEASE.md` passes. | +| 2 | `vcs.md` mentions `next`, has `from`/`PR target` columns, and links to `RELEASE.md`; no mermaid flow block present. | +| 3 | `GOVERNANCE.md` names `next` in branch protection and links `RELEASE.md`. | +| 4 | `CONTRIBUTING.md` states `next` as the default PR target and links `RELEASE.md`. | +| all | `grep -RIl "next.*integration\|weekly promotion" RELEASE.md GOVERNANCE.md aidd_docs/memory/vcs.md` shows the flow narrative only in `RELEASE.md`. | diff --git a/aidd_docs/tasks/2026_06/sandbox-test-runbook.md b/aidd_docs/tasks/2026_06/sandbox-test-runbook.md new file mode 100644 index 00000000..661526b7 --- /dev/null +++ b/aidd_docs/tasks/2026_06/sandbox-test-runbook.md @@ -0,0 +1,171 @@ +# Sandbox test runbook - rolling weekly releases + +Validates the `main`/`next` release model end to end before it touches the real +repo. Two layers: a local git simulation (offline, deterministic, no GitHub) for +the back-merge logic, and a GitHub sandbox repo for the parts that need real +Actions, rulesets, the App, and release-please. + +> ATTENTION +> - Never run the GitHub scenarios against the production repo first. The +> auto-merge step merges the Release PR automatically; a wrong setup ships. +> - Use a throwaway sandbox repo and delete it at the end. +> - The App token has write + bypass. Treat its private key as a secret. + +## Real-repo rollout order (critical) + +The auto-merge step in `ci.yml` goes live the instant that file lands on `main`, +independent of everything else. Today release PRs are human-merged, which +effectively batches many pushes into one shipped release. The moment the step is +active it flips to release-on-every-push-to-main. If features still land directly +on `main` (the current habit) and `next` does not yet steer them, that means a +release per feature push and the leak window stays wide. Also `back-merge.yml` +triggers on `release: published` and checks out `next`; if a release fires before +`next` exists, it errors. + +So land the pieces in this order on the real repo, not all at once: + +1. Create `next` (`git branch next main && git push -u origin next`). +2. Apply `next.json` ruleset and confirm `main` blocks direct pushes (features + must target `next`). Verify direct-to-main is actually rejected. +3. Only then merge the PR that adds the `ci.yml` auto-merge step and + `back-merge.yml`. +4. Remove `version` from `marketplace.json` any time (independent, schema-safe). + +Verified: at the pinned action SHA, `prs_created` and `pr` (with `.number`) are +real `release-please-action` v5 outputs, so the step's guard and PR lookup are +correct by name. Whether the App merges immediately is the runtime unknown - see +Scenario 2 WATCH. + +## Layer 1 - Local git simulation (run now, offline) + +Covers the `back-merge.yml` git logic: clean back-merge, conflict -> abort, +hotfix back-merge. No GitHub, no Actions. Runs in an isolated temp dir. + +```bash +set -euo pipefail +SB=$(mktemp -d /tmp/aidd-sandbox.XXXXXX); trap 'rm -rf "$SB"' EXIT +cd "$SB"; git init -q --bare origin.git; git clone -q origin.git work; cd work +git config user.email t@t; git config user.name tester; git config commit.gpgsign false + +printf '# Changelog\n\nseed\n' > CHANGELOG.md; echo "code v0" > app.txt +git add -A; git commit -qm "chore: seed"; git branch -M main; git push -q origin main +git checkout -q -b next; git push -q origin next + +# A) happy: feat -> next, promote next -> main, release commit on main, back-merge +git checkout -q next; echo "feature" >> app.txt; git commit -qam "feat: add x"; git push -q origin next +git checkout -q main; git merge -q --no-edit next; git push -q origin main +printf '# Changelog\n\n## 1.1.0\n- feat: add x\n\nseed\n' > CHANGELOG.md +git commit -qam "chore(main): release 1.1.0"; git push -q origin main +git checkout -q next; git fetch -q origin main +git merge --no-edit origin/main >/dev/null 2>&1 && { git push -q origin next; echo "A clean back-merge OK"; } || { echo "A unexpected conflict"; exit 1; } + +# B) conflict: same CHANGELOG line diverges -> merge must abort cleanly +git checkout -q next; printf '# Changelog\n\nNEXT-SIDE EDIT\n' > CHANGELOG.md +git commit -qam "docs: tweak changelog"; git push -q origin next +git checkout -q main; printf '# Changelog\n\nMAIN RELEASE EDIT\n' > CHANGELOG.md +git commit -qam "chore(main): release 1.2.0"; git push -q origin main +git checkout -q next; git fetch -q origin main +git merge --no-edit origin/main >/dev/null 2>&1 && { echo "B should have conflicted"; exit 1; } || { git merge --abort; echo "B conflict -> abort OK (PR path)"; } +``` + +Expected: `A clean back-merge OK`, `A` next has the release commit, `B conflict +-> abort OK`. A failed assertion means the workflow's git logic is wrong. + +## Layer 2 - GitHub sandbox (needs a real repo) + +These cannot be tested locally: release-please opening/auto-merging the Release +PR, ruleset enforcement, App bypass, workflow triggering on `push`/`release`. + +### Setup + +1. Create a throwaway repo, e.g. `ai-driven-dev/release-sandbox` (private). +2. Copy in: `.github/workflows/ci.yml`, `.github/workflows/back-merge.yml`, + `release-please-config.json`, `.release-please-manifest.json`, + `.claude-plugin/`, `plugins/` (a trimmed set is fine), and + `.github/rulesets/{main,next}.json`. +3. Install the AIDD bot App (or a dedicated test App) on the sandbox; set repo + secrets `AIDD_BOT_APP_ID`, `AIDD_BOT_PRIVATE_KEY`. +4. Push `main`, then create `next`: `git branch next main && git push -u origin next`. +5. Apply the rulesets (GitHub does not auto-sync `.github/rulesets/*.json`): + ```bash + gh api -X POST repos/ai-driven-dev/release-sandbox/rulesets --input .github/rulesets/main.json + gh api -X POST repos/ai-driven-dev/release-sandbox/rulesets --input .github/rulesets/next.json + ``` + Fix the `bypass_actors[].actor_id` to the sandbox App/team ids first. +6. Leave repo "Allow auto-merge" OFF for the primary test (immediate merge). Turn + it ON only when testing the `--auto` fallback. + +### Scenario 1 - setup integrity + +- `git ls-remote --heads origin next` returns a ref. +- `gh api repos//rulesets` lists `main protection` and `next protection` active. +- The App appears in each ruleset's bypass list. + +### Scenario 2 - happy weekly release + +1. `feat/x` off `next` -> PR to `next` -> merge. Expect: checks run, merge allowed + with 0 required reviews on `next`. +2. Promotion PR `next` -> `main` -> merge. +3. Observe in Actions/PRs/Releases, and ASSERT each (a silent no-op must fail + the test loudly, not look fine): + - release-please opens a `chore(main): release ...` PR on `main`. + - the "Auto-merge the Release PR" step runs AND the Release PR shows + `MERGED` by the App within one CI run. Assert it, e.g. + `gh pr list --state merged --search "release in:title" --json mergedBy,title` + shows the App as `mergedBy`. If the step's `if` was silently false (wrong + output name) the PR stays open and this assertion fails - that is the point. + - tags created, GitHub Releases created, `build-*` jobs attach the bundles. + - `back-merge.yml` fires on `release: published` and `next` advances to `main`. + +RESOLVED in a sandbox probe (ai-driven-dev/aidd-release-sandbox): +- A plain `gh pr merge --squash` is REFUSED by the branch policy ("base branch + policy prohibits the merge") even for a bypass actor with unmet review + + pending required checks. `gh pr merge --squash --admin` performs the override + merge that the ruleset bypass permits, and succeeds. The ci.yml step therefore + uses `--admin`. (`--auto` is not viable here: a bot can never satisfy the + required review, so it would wait forever, and the repo had `allow_auto_merge` + off.) + +STILL WATCH: +- Confirm the App-token `--admin` merge works for the App specifically (the probe + used an admin user; the App is a bypass actor in the prod ruleset, which should + grant the same override - confirm on the first real run). +- Confirm the App-token merge RE-FIRES the downstream jobs (build + back-merge). A + `GITHUB_TOKEN` merge would not; that is why the step uses the App token. + +### Scenario 3 - hotfix out of cycle + +1. `hotfix/y` off `main` -> PR to `main` -> merge. +2. Expect: release-please cuts a dedicated patch release; back-merge runs. + +### Scenario 4 - back-merge conflict + +1. On `next`, commit a change to `CHANGELOG.md` top lines; push. +2. Trigger a release on `main` that edits the same region. +3. Expect: `back-merge.yml` aborts the merge and opens a + `chore: back-merge main into next (conflicts)` PR instead of pushing. + +### Scenario 5 - new-user leak window (manual observation) + +- During Scenario 2, between the promotion merge and the Release PR auto-merge, + `main` HEAD briefly holds unbumped code. Confirm the window is only one CI run + (auto-merge is near-immediate), not human time. If the merge is not immediate, + this is the leak the model exists to avoid - fix per Scenario 2 WATCH. + +### Teardown + +```bash +gh repo delete ai-driven-dev/release-sandbox --yes +``` + +## What each layer proves + +| Concern | Layer 1 (local) | Layer 2 (GitHub) | +| --- | --- | --- | +| back-merge clean / conflict / hotfix git logic | yes | yes | +| release-please opens Release PR | no | yes | +| auto-merge step actually merges | no | yes | +| immediate vs `--auto` merge for the bypass App | no | yes (the key unknown) | +| App token re-fires downstream workflows | no | yes | +| ruleset enforcement + App bypass on `next` | no | yes | +| marketplace.json still schema-valid after version removal | already checked (`check-jsonschema`) | n/a |