Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
44 changes: 44 additions & 0 deletions .github/STABILITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# wizrd-cli composite stability contract

Everything under `.github/workflows/*.yml` and `.github/actions/*/action.yml` is **public API** — consumed by ~20 Digitaliko repos via `@v1` references.

## What's additive (stays on @v1)

- Adding a new composite workflow file.
- Adding a new composite action.
- Adding a new input to an existing composite, with a default that preserves prior behavior.
- Adding a new output to an existing composite.
- Adding a new optional secret to an existing composite (with a sensible fallback if missing).
- Changing the internal implementation of a composite as long as inputs/outputs/secrets stay the same.

## What's breaking (requires cutting @v2)

- Removing or renaming a composite workflow or action.
- Removing or renaming an existing input, output, or secret.
- Changing the type of an existing input.
- Changing the default of an existing input in a way that flips behavior.
- Changing required permissions or `runs-on` target.
- Changing the meaning of an existing label transition.

## Bump rhythm

- Additive changes: PR + merge, then `git tag -fa v1 && git push --force-with-lease origin v1`.
- Breaking changes: cut `v2` tag, leave `v1` floating where it is. Email/Slack-equivalent the rollout — every consumer migrates on their own timeline.

## Labels are also public API

The `wizrd:*` label namespace is contract. Renaming `wizrd:plan` → `wizrd:planning` is breaking. Same `v2` rule applies.

## Deprecated (removal in Phase G after fleet rollout)

These composites are superseded by the pipeline stages and will be removed once every repo is on the pipeline:

- `.github/workflows/wizrd-agent.yml` — reactive `@claude`/`ai: implement` one-shot. Replaced by the full triage → … → verify cascade.
- `.github/actions/wizrd-agent/` — same logic as composite action. Same replacement.
- `.github/workflows/wizrd-review.yml` — one-shot multi-agent review. Folded into `wizrd-stage-review.yml`, which invokes the same `/code-review` plugin inside the review loop.

Until removed they keep working; the deprecation is a *flag*, not a behavior change.

## When in doubt

Treat composite inputs and labels like database columns: easy to add, painful to remove.
91 changes: 91 additions & 0 deletions .github/actions/wizrd-transition/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: 'Wizrd Stage Transition'
description: 'Removes the current stage label and applies the next label (yolo) or awaiting-approval (human-approval) or needs-judgment (failure). Optionally dispatches the next workflow.'

inputs:
issue_number:
description: 'Issue or PR number to transition.'
required: true
current_label:
description: 'Label to remove (e.g. wizrd:triage).'
required: true
next_label:
description: 'Label to add on success-yolo. Empty for terminal stages.'
required: false
default: ''
next_stage:
description: 'Semantic stage name to dispatch (e.g. plan, implement). Empty = no dispatch.'
required: false
default: ''
pipeline_workflow:
description: 'Basename of the per-repo pipeline entry workflow (default wizrd-pipeline.yml).'
required: false
default: 'wizrd-pipeline.yml'
mode:
description: 'success-yolo | success-human-approval | success-terminal | failure'
required: true
awaiting_label:
description: 'Label to apply on human-approval success. Default wizrd:awaiting-approval.'
required: false
default: 'wizrd:awaiting-approval'
needs_judgment_label:
description: 'Label to apply on failure. Default wizrd:needs-judgment.'
required: false
default: 'wizrd:needs-judgment'

runs:
using: composite
steps:
- name: Transition labels
shell: bash
env:
GH_TOKEN: ${{ github.token }}
NUM: ${{ inputs.issue_number }}
CUR: ${{ inputs.current_label }}
NXT: ${{ inputs.next_label }}
AWL: ${{ inputs.awaiting_label }}
NEJ: ${{ inputs.needs_judgment_label }}
MODE: ${{ inputs.mode }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
# Always try to remove the current label. Tolerate already-gone.
gh api -X DELETE "repos/$REPO/issues/$NUM/labels/$CUR" 2>/dev/null || true

case "$MODE" in
success-yolo)
if [ -n "$NXT" ]; then
gh api -X POST "repos/$REPO/issues/$NUM/labels" -f "labels[]=$NXT"
fi
;;
success-human-approval)
gh api -X POST "repos/$REPO/issues/$NUM/labels" -f "labels[]=$AWL"
;;
success-terminal)
: # nothing to add
;;
failure)
gh api -X POST "repos/$REPO/issues/$NUM/labels" -f "labels[]=$NEJ"
;;
*)
echo "Unknown mode: $MODE" >&2
exit 1
;;
esac

- name: Dispatch next workflow
if: inputs.mode == 'success-yolo' && inputs.next_stage != ''
shell: bash
env:
GH_TOKEN: ${{ github.token }}
NUM: ${{ inputs.issue_number }}
STAGE: ${{ inputs.next_stage }}
PWF: ${{ inputs.pipeline_workflow }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
# Required: label-add via GITHUB_TOKEN does NOT fire labeled events.
# Route via the per-repo pipeline workflow with the stage input.
gh workflow run "$PWF" \
--repo "$REPO" \
--field stage="$STAGE" \
--field issue_number="$NUM"
126 changes: 126 additions & 0 deletions .github/workflows/wizrd-stage-doc-gardening.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Wizrd Stage — Doc Gardening (post-merge)

# Terminal stage: runs on every merged PR. No label cascade, no chaining.
# Self-loop guard: skips if the merged PR's head branch starts with
# docs/post-merge-, otherwise doc-gardening would trigger itself forever.

on:
workflow_call:
inputs:
pr_number:
description: 'PR number of the just-merged PR.'
required: true
type: string
docs_root:
description: 'Root path the agent may edit (default: docs). For L1 wizrds, override to clients/<x>/knowledge-base.'
required: false
type: string
default: 'docs'
secrets:
claude_token:
required: true

jobs:
doc-gardening:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
actions: read
id-token: write
concurrency:
group: wizrd-doc-gardening-${{ inputs.pr_number }}
cancel-in-progress: false
steps:
- name: Self-loop guard
id: guard
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ inputs.pr_number }}
REPO: ${{ github.repository }}
shell: bash
run: |
set -euo pipefail
head=$(gh pr view "$PR" --repo "$REPO" --json headRefName --jq .headRefName)
if printf '%s' "$head" | grep -qE '^docs/post-merge-'; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Skipping — merged PR is itself a doc-gardening PR ($head)."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- uses: actions/checkout@v5
if: steps.guard.outputs.skip == 'false'
with:
fetch-depth: 0

- name: Run doc-gardener
if: steps.guard.outputs.skip == 'false'
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.claude_token }}
base_branch: ${{ github.event.repository.default_branch }}
branch_prefix: 'docs/post-merge-'
branch_name_template: 'docs/post-merge-${{ inputs.pr_number }}'
allowed_bots: 'github-actions'
additional_permissions: |
actions: read
claude_args: '--allowedTools "Read,Glob,Grep,Bash,Edit,Write"'
prompt: |
Stage: doc-gardening (post-merge). PR: ${{ inputs.pr_number }}.

A pull request just merged. Your job: keep `${{ inputs.docs_root }}/`
aligned with the codebase. This is a TERMINAL stage — do NOT
transition labels, do NOT dispatch other workflows, do NOT modify
the merged PR's content.

1. Read the merged PR's metadata + diff:
gh pr view ${{ inputs.pr_number }} --json mergeCommit,baseRefName,headRefName,title,body
gh pr diff ${{ inputs.pr_number }}

2. Classify the change as SIGNIFICANT or TRIVIAL.

SIGNIFICANT examples:
- new public API / module / CLI flag / env var
- behavior change visible to users or integrators
- breaking change or new dependency surface
- architecture shift
- new convention worth documenting

TRIVIAL examples:
- typo / formatting / comment-only
- internal refactor with no public-surface change
- test-only change
- dependency bump with no API impact

3. If TRIVIAL: post a single comment on the merged PR:
"doc-gardening skipped — change is internal/trivial."
then exit. No branch, no PR.

4. If SIGNIFICANT:
- Read `${{ inputs.docs_root }}/` to learn the doc structure.
- If `${{ inputs.docs_root }}/` does not exist and the change
warrants it, bootstrap with a README indexing per-module pages.
- For monorepos, prefer `${{ inputs.docs_root }}/<module>/<topic>.md`
over giant catch-all files. Cross-link liberally.
- Create branch `docs/post-merge-${{ inputs.pr_number }}` off
the default branch. Edit ONLY files under `${{ inputs.docs_root }}/`.
- Commit, push, open a NEW draft PR titled
"docs: update for #${{ inputs.pr_number }}" describing what was
added/changed and which merged PR drove it.

Do NOT touch code outside `${{ inputs.docs_root }}/`. Do NOT reopen
or modify the merged PR beyond the optional skip-comment in step 3.

- name: Comment on merged PR on failure
if: failure() && steps.guard.outputs.skip == 'false'
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ inputs.pr_number }}
REPO: ${{ github.repository }}
shell: bash
run: |
set -euo pipefail
gh pr comment "$PR" --repo "$REPO" --body \
"doc-gardening failed — see workflow run \`${{ github.run_id }}\`. No \`wizrd:needs-judgment\` label applied (post-merge stage has no cascade)."
82 changes: 82 additions & 0 deletions .github/workflows/wizrd-stage-fix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Wizrd Stage — Fix (review-loop companion)

# Dispatched only from wizrd-stage-review.yml. Never triggered directly.

on:
workflow_call:
inputs:
issue_number:
required: true
type: string
pipeline_workflow:
required: false
type: string
default: 'wizrd-pipeline.yml'
secrets:
claude_token:
required: true

jobs:
fix:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
actions: write
id-token: write
concurrency:
group: wizrd-fix-${{ inputs.issue_number }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
ref: refs/pull/${{ inputs.issue_number }}/head

- name: Run fixer
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.claude_token }}
allowed_bots: 'github-actions'
additional_permissions: |
actions: read
claude_args: '--allowedTools "Read,Glob,Grep,Bash,Edit,Write"'
prompt: |
Stage: review (fix pass). PR: ${{ inputs.issue_number }}.

The most recent PR review requested changes. Read the review body
and any line-level comments, then address each item: edit the
code on the PR branch, commit with a clear message, push.

Do NOT open a new PR. Do NOT resolve review threads.
Keep scope tight: only fix what the review asked for.

- name: Dispatch review on success
if: success()
env:
GH_TOKEN: ${{ github.token }}
NUM: ${{ inputs.issue_number }}
PWF: ${{ inputs.pipeline_workflow }}
REPO: ${{ github.repository }}
shell: bash
run: |
set -euo pipefail
gh workflow run "$PWF" --repo "$REPO" --field stage="review" --field issue_number="$NUM"

- name: Park on failure
if: failure()
env:
GH_TOKEN: ${{ github.token }}
NUM: ${{ inputs.issue_number }}
REPO: ${{ github.repository }}
shell: bash
run: |
set -euo pipefail
iter=$(gh api "repos/$REPO/issues/$NUM/labels" --jq '.[].name' | \
grep -oE '^wizrd:review-iter-[0-9]+$' | head -n1 || echo "")
if [ -n "$iter" ]; then
gh api -X DELETE "repos/$REPO/issues/$NUM/labels/$iter" 2>/dev/null || true
fi
gh api -X DELETE "repos/$REPO/issues/$NUM/labels/wizrd:review" 2>/dev/null || true
gh api -X POST "repos/$REPO/issues/$NUM/labels" -f "labels[]=wizrd:needs-judgment"
Loading