feat: add work-order orchestration scripts (Phase 3)

- create-work-order.sh: auto-fills CMS commit, date, contracts version
- execute-work-order.sh: updates contracts on sv-frontend, provides
  implementation instructions for Claude Code
- Updated CLAUDE.md with complete work order lifecycle documentation
- Added pnpm shortcuts: wo:create, wo:execute

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Porwoll 2026-02-15 22:30:57 +00:00
parent a6b8a0f914
commit d4e1ef1035
4 changed files with 326 additions and 1 deletions

107
CLAUDE.md
View file

@ -63,7 +63,112 @@ When CMS collections or blocks change:
## Work Orders
The `work-orders/` directory contains coordination files for propagating CMS changes to frontends. See `work-orders/_template.md` for the format.
The `work-orders/` directory is the coordination mechanism between the CMS (sv-payload) and all frontends (sv-frontend / Plesk production). When a CMS change requires frontend updates, a work order is created.
### Structure
```
work-orders/
├── _template.md # Template for new work orders
├── YYYY-MM-DD-slug.md # Active work orders
└── completed/ # Archived completed work orders
```
### Creating a Work Order (on sv-payload)
```bash
cd ~/payload-contracts
# After CMS changes: extract types + create work order
./scripts/create-work-order.sh "Add stats-block" --extract
# Or manually: create from template
cp work-orders/_template.md work-orders/$(date +%Y-%m-%d)-my-change.md
# Edit, then commit + push
```
### Executing a Work Order (on sv-frontend)
When you receive a work order (via git pull on the contracts repo):
1. **Read the work order** in `work-orders/` — it contains the exact TypeScript interface, implementation steps, and reference code
2. **Update contracts**: `pnpm update @c2s/payload-contracts` (or `pnpm install`)
3. **Implement** the changes described in the work order
4. **Verify**: `pnpm lint && pnpm build`
5. **Commit and push** to `develop`
Or use the orchestration script from sv-payload:
```bash
./scripts/execute-work-order.sh work-orders/2026-02-15-add-stats-block.md
```
### Work Order Lifecycle
```
sv-payload sv-frontend
━━━━━━━━━━ ━━━━━━━━━━━
1. Change CMS block/collection
2. pnpm payload generate:types
3. cd ~/payload-contracts
4. pnpm extract
5. ./scripts/create-work-order.sh "Title"
6. Edit work order (add interface, steps)
7. git add -A && git commit && git push
8. git pull (contracts repo)
9. Read work order
10. pnpm update @c2s/payload-contracts
11. Implement block/changes
12. pnpm lint && pnpm build
13. git commit && git push (develop)
14. Verify result
15. Move to completed/:
mv work-orders/X.md work-orders/completed/
git commit && git push
```
### Block Implementation Pattern
When implementing a new block from a work order, follow this pattern:
```typescript
// src/components/blocks/MyNewBlock.tsx
import type { BlockByType } from '@c2s/payload-contracts/types'
type MyNewBlockData = BlockByType<'my-new-block'>
interface MyNewBlockProps {
block: MyNewBlockData
}
export function MyNewBlock({ block }: MyNewBlockProps) {
// block is fully typed from the contracts
return (
<section>
<h2>{block.title}</h2>
{/* ... */}
</section>
)
}
```
Then register in the block renderer:
```typescript
// src/components/blocks/index.tsx
import { MyNewBlock } from './MyNewBlock'
const blockComponents = {
// ... existing blocks
'my-new-block': MyNewBlock,
}
```
## Scripts
| Script | Location | Purpose |
|--------|----------|---------|
| `pnpm extract` | sv-payload | Extract types from CMS payload-types.ts |
| `create-work-order.sh` | sv-payload | Create a new work order with metadata |
| `execute-work-order.sh` | sv-payload | Orchestrate work order execution on sv-frontend |
## Git Workflow

View file

@ -35,6 +35,8 @@
"dev": "tsc --watch",
"typecheck": "tsc --noEmit",
"extract": "tsx scripts/extract-types.ts",
"wo:create": "bash scripts/create-work-order.sh",
"wo:execute": "bash scripts/execute-work-order.sh",
"preinstall": "true",
"prepare": "true"
},

100
scripts/create-work-order.sh Executable file
View file

@ -0,0 +1,100 @@
#!/usr/bin/env bash
#
# create-work-order.sh — Create a work order after CMS changes
#
# Usage:
# ./scripts/create-work-order.sh "Add stats-block" [--extract]
#
# This script:
# 1. Optionally re-extracts types from CMS (--extract flag)
# 2. Creates a work order from template with pre-filled metadata
# 3. Opens it for editing (or prints path if no $EDITOR)
#
# Run from the payload-contracts repo root on sv-payload.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(dirname "$SCRIPT_DIR")"
WO_DIR="${ROOT}/work-orders"
TEMPLATE="${WO_DIR}/_template.md"
CMS_DIR="${ROOT}/../payload-cms"
# --- Args ---
TITLE="${1:-}"
DO_EXTRACT=false
for arg in "$@"; do
case "$arg" in
--extract) DO_EXTRACT=true ;;
esac
done
if [ -z "$TITLE" ] || [ "$TITLE" = "--extract" ]; then
echo "Usage: $0 \"Work Order Title\" [--extract]"
echo ""
echo "Options:"
echo " --extract Re-extract types from CMS before creating work order"
echo ""
echo "Example:"
echo " $0 \"Add stats-block to page layout\" --extract"
exit 1
fi
# --- Optional: Re-extract types ---
if [ "$DO_EXTRACT" = true ]; then
echo "=== Extracting types from CMS ==="
cd "$ROOT"
pnpm extract
echo ""
fi
# --- Get CMS commit info ---
CMS_COMMIT="unknown"
CMS_MSG="unknown"
if [ -d "$CMS_DIR/.git" ]; then
CMS_COMMIT=$(git -C "$CMS_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")
CMS_MSG=$(git -C "$CMS_DIR" log -1 --format="%s" 2>/dev/null || echo "unknown")
fi
# --- Get contracts commit ---
CONTRACTS_COMMIT=$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo "unknown")
# --- Generate filename ---
DATE=$(date +%Y-%m-%d)
SLUG=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
FILENAME="${DATE}-${SLUG}.md"
FILEPATH="${WO_DIR}/${FILENAME}"
if [ -f "$FILEPATH" ]; then
echo "Error: Work order already exists: ${FILEPATH}"
exit 1
fi
# --- Create work order from template ---
sed \
-e "s/\[Titel\]/${TITLE}/" \
-e "s/\[hash\] (\[Beschreibung\])/${CMS_COMMIT} (${CMS_MSG})/" \
-e "s/\[version \/ commit hash\]/${CONTRACTS_COMMIT}/" \
-e "s/YYYY-MM-DD/${DATE}/" \
"$TEMPLATE" > "$FILEPATH"
echo "=== Work order created ==="
echo "File: ${FILEPATH}"
echo "CMS commit: ${CMS_COMMIT} (${CMS_MSG})"
echo "Contracts: ${CONTRACTS_COMMIT}"
echo ""
# --- Open in editor if available ---
if [ -n "${EDITOR:-}" ]; then
"$EDITOR" "$FILEPATH"
else
echo "Edit the work order:"
echo " nano ${FILEPATH}"
echo ""
echo "Then commit and push:"
echo " cd ${ROOT}"
echo " git add work-orders/${FILENAME}"
echo " git commit -m \"wo: ${TITLE}\""
echo " git push origin main"
fi

118
scripts/execute-work-order.sh Executable file
View file

@ -0,0 +1,118 @@
#!/usr/bin/env bash
#
# execute-work-order.sh — Execute a work order on sv-frontend
#
# Usage (from sv-payload):
# ./scripts/execute-work-order.sh <work-order-file> [frontend-repo]
#
# Examples:
# ./scripts/execute-work-order.sh work-orders/2026-02-15-add-stats-block.md
# ./scripts/execute-work-order.sh work-orders/2026-02-15-add-stats-block.md frontend.porwoll.de
#
# This script:
# 1. Reads the work order
# 2. SSHs to sv-frontend
# 3. Updates payload-contracts dependency
# 4. Launches Claude Code with the work order as prompt (or prints instructions)
#
# Prerequisites:
# - SSH access to sv-frontend configured (~/.ssh/config)
# - Work order committed and pushed to payload-contracts main branch
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(dirname "$SCRIPT_DIR")"
WO_FILE="${1:-}"
TARGET_REPO="${2:-}"
if [ -z "$WO_FILE" ]; then
echo "Usage: $0 <work-order-file> [frontend-repo]"
echo ""
echo "Available work orders:"
ls -1 "${ROOT}/work-orders/"*.md 2>/dev/null | grep -v _template || echo " (none)"
echo ""
echo "Example:"
echo " $0 work-orders/2026-02-15-add-stats-block.md"
echo " $0 work-orders/2026-02-15-add-stats-block.md frontend.porwoll.de"
exit 1
fi
# Resolve relative path
if [[ ! "$WO_FILE" = /* ]]; then
WO_FILE="${ROOT}/${WO_FILE}"
fi
if [ ! -f "$WO_FILE" ]; then
echo "Error: Work order not found: ${WO_FILE}"
exit 1
fi
WO_CONTENT=$(cat "$WO_FILE")
WO_NAME=$(basename "$WO_FILE")
# --- Extract affected frontends from work order ---
FRONTENDS=()
while IFS= read -r line; do
repo=$(echo "$line" | grep -oP 'frontend\.[a-z.-]+' || true)
if [ -n "$repo" ]; then
FRONTENDS+=("$repo")
fi
done <<< "$(echo "$WO_CONTENT" | grep -E '^\- \[[ x]\]')"
if [ -n "$TARGET_REPO" ]; then
FRONTENDS=("$TARGET_REPO")
fi
if [ ${#FRONTENDS[@]} -eq 0 ]; then
echo "Warning: No affected frontends found in work order."
echo "Specify a target repo: $0 $1 frontend.porwoll.de"
exit 1
fi
echo "=== Work Order: ${WO_NAME} ==="
echo "Affected frontends: ${FRONTENDS[*]}"
echo ""
# --- Process each frontend ---
for REPO in "${FRONTENDS[@]}"; do
echo "--- Processing: ${REPO} ---"
# Check if repo exists on sv-frontend
if ! ssh sv-frontend "test -d ~/\${REPO}/.git" 2>/dev/null; then
echo " Skip: ~/${REPO} not found on sv-frontend"
continue
fi
# Update contracts dependency
echo " Updating @c2s/payload-contracts..."
ssh sv-frontend "cd ~/${REPO} && pnpm update @c2s/payload-contracts 2>&1" || {
echo " Warning: pnpm update failed, trying install..."
ssh sv-frontend "cd ~/${REPO} && pnpm install 2>&1"
}
# Pull latest from contracts
ssh sv-frontend "cd ~/payload-contracts 2>/dev/null && git pull origin main 2>&1" || true
echo ""
echo " Contracts updated. To implement the work order:"
echo ""
echo " Option A — Run Claude Code on sv-frontend:"
echo " ssh sv-frontend"
echo " cd ~/${REPO}"
echo " claude"
echo " # Then paste: Implement work order ${WO_NAME}"
echo ""
echo " Option B — Remote from sv-payload:"
echo " ssh sv-frontend \"cd ~/${REPO} && claude --print 'Implement this work order: $(echo "$WO_CONTENT" | head -5 | tr '\n' ' ')...'\""
echo ""
done
echo "=== Done ==="
echo ""
echo "After implementation, verify and complete:"
echo " 1. ssh sv-frontend \"cd ~/<repo> && pnpm build\""
echo " 2. Move work order to completed/:"
echo " mv ${WO_FILE} ${ROOT}/work-orders/completed/${WO_NAME}"
echo " cd ${ROOT} && git add -A && git commit -m 'wo: complete ${WO_NAME}' && git push"