mirror of
https://github.com/complexcaresolutions/payload-contracts.git
synced 2026-03-17 18:43:48 +00:00
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:
parent
a6b8a0f914
commit
d4e1ef1035
4 changed files with 326 additions and 1 deletions
107
CLAUDE.md
107
CLAUDE.md
|
|
@ -63,7 +63,112 @@ When CMS collections or blocks change:
|
||||||
|
|
||||||
## Work Orders
|
## 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
|
## Git Workflow
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@
|
||||||
"dev": "tsc --watch",
|
"dev": "tsc --watch",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"extract": "tsx scripts/extract-types.ts",
|
"extract": "tsx scripts/extract-types.ts",
|
||||||
|
"wo:create": "bash scripts/create-work-order.sh",
|
||||||
|
"wo:execute": "bash scripts/execute-work-order.sh",
|
||||||
"preinstall": "true",
|
"preinstall": "true",
|
||||||
"prepare": "true"
|
"prepare": "true"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
100
scripts/create-work-order.sh
Executable file
100
scripts/create-work-order.sh
Executable 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
118
scripts/execute-work-order.sh
Executable 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"
|
||||||
Loading…
Reference in a new issue