chore: clean up workflow YAML — bun→pnpm, enum→const, deduplicate
CI / check (push) Failing after 3m6s

- solve-issue.yaml: bun→pnpm (5 refs), examples/ is now canonical
- Delete redundant workflows/solve-issue.yaml and .workflows/solve-issue.yaml
- analyze-topic.yaml + eval-simple.yaml: enum→const for $status
- Archive normalize-bun-monorepo.yaml and e2e-walkthrough.yaml to legacy-packages/

Closes #137
小橘 🍊
This commit is contained in:
2026-06-06 10:56:28 +00:00
parent aa732f5466
commit 8ddada5879
7 changed files with 9 additions and 586 deletions
@@ -0,0 +1,286 @@
name: "e2e-walkthrough"
description: "End-to-end walkthrough of uwf CLI. Dogfooding: uwf tests uwf. Each role validates a phase of the CLI surface inside an isolated Docker container."
roles:
bootstrap:
description: "Start Docker container with isolated storage, verify uwf is runnable"
goal: "You are an E2E test runner. Set up an isolated Docker environment and verify basic uwf functionality."
capabilities:
- docker
- shell
procedure: |
1. Start a Docker container with isolated storage.
IMPORTANT: Mount the source code READ-ONLY to prevent the container
from overwriting host files (e.g. bun install would replace macOS bun with Linux bun).
Use a container-local HOME so bun/npm installs stay inside the container.
Add host.docker.internal mapping for LLM API access from inside the container.
```
docker run -d --name uwf-e2e-$$ \
-v "$(pwd):/workspace:ro" \
-e HOME=/root \
-e UWF_HOME=/tmp/uwf-e2e-storage \
--add-host=host.docker.internal:host-gateway \
-w /workspace \
node:22-bookworm \
sleep infinity
```
NOTE: Run this from the workflow monorepo root directory.
On macOS Docker Desktop, host.docker.internal is already available;
--add-host ensures it also works on Linux Docker.
2. Inside the container, copy source to a writable location, install bun, install deps,
then `bun link` all packages so that `uwf`, `uwf-hermes`, `uwf-builtin` are on PATH:
```
docker exec uwf-e2e-$$ bash -c '
# Copy source to writable location (mount is read-only)
cp -r /workspace /root/workflow
# Install bun
curl -fsSL https://bun.sh/install | bash
export PATH="$HOME/.bun/bin:$PATH"
# Isolated storage
mkdir -p $UWF_HOME
# Install workspace deps
cd /root/workflow && bun install
# bun link each package that has a bin entry
cd packages/cli && bun link && cd ../..
cd packages/agent-hermes && bun link && cd ../..
cd packages/agent-builtin && bun link && cd ../..
'
```
3. Verify all three commands are available inside the container:
```
docker exec uwf-e2e-$$ bash -c 'export PATH="$HOME/.bun/bin:$PATH" && uwf --version'
docker exec uwf-e2e-$$ bash -c 'export PATH="$HOME/.bun/bin:$PATH" && uwf-hermes --help'
docker exec uwf-e2e-$$ bash -c 'export PATH="$HOME/.bun/bin:$PATH" && uwf-builtin --help'
```
4. Copy host uwf config into the container's isolated storage.
The host config contains provider credentials and model settings needed for LLM calls.
Also rewrite any localhost URLs to host.docker.internal so the container can reach host services.
```
docker cp ~/.uwf/config.yaml uwf-e2e-$$:/tmp/uwf-e2e-storage/config.yaml 2>/dev/null || true
docker exec uwf-e2e-$$ bash -c '
if [ -f $UWF_HOME/config.yaml ]; then
sed -i "s|localhost|host.docker.internal|g; s|127\.0\.0\.1|host.docker.internal|g" \
$UWF_HOME/config.yaml
fi
'
```
Report the container name and confirm uwf + agents are working.
Set containerName to the Docker container name for subsequent roles.
output: "Report uwf version and container readiness. Set $status to pass with containerName, or fail with error."
frontmatter:
oneOf:
- properties:
$status: { const: "pass" }
containerName: { type: string }
required: [$status, containerName]
- properties:
$status: { const: "fail" }
error: { type: string }
required: [$status, error]
config-and-registry:
description: "Validate uwf config commands and workflow registration"
goal: "You are an E2E test runner. Validate uwf config operations and workflow registration inside the Docker container."
capabilities:
- docker
- shell
procedure: |
Use the container from the previous step (containerName is in your prompt).
All commands run via: `docker exec <containerName> bash -c '...'`
All commands use `uwf` (installed via `bun link` inside the container).
Remember to set env vars in each exec:
export PATH="$HOME/.bun/bin:$PATH"
export UWF_HOME=/tmp/uwf-e2e-storage
Config tests:
1. `uwf config list` — verify it returns valid JSON
2. `uwf config set models.test.name test-model` — set a test key
3. `uwf config get models.test.name` — verify it returns "test-model"
Workflow registration tests:
4. `uwf workflow add /root/workflow/examples/debate.yaml` — register a workflow (use debate.yaml as it has no $SUSPEND dependency)
5. Verify the output contains a hash
6. `uwf workflow list` — verify non-empty array
7. Capture the workflow name from the list
8. `uwf workflow show <name>` — verify it returns roles
Report all test results with pass/fail counts.
output: "Report test results. Set $status to pass (with workflowName and containerName) or fail."
frontmatter:
oneOf:
- properties:
$status: { const: "pass" }
workflowName: { type: string }
containerName: { type: string }
required: [$status, workflowName, containerName]
- properties:
$status: { const: "fail" }
error: { type: string }
containerName: { type: string }
required: [$status, error, containerName]
thread-ops:
description: "Test thread start, list, show, and exec"
goal: "You are an E2E test runner. Validate thread creation and execution inside the Docker container."
capabilities:
- docker
- shell
procedure: |
Use the container (containerName) and workflow (workflowName) from your prompt.
All commands via: `docker exec <containerName> bash -c '...'`
Set env: PATH="$HOME/.bun/bin:$PATH" UWF_HOME=/tmp/uwf-e2e-storage
1. `uwf thread start <workflowName> -p 'E2E test: what is 2+2?'` — capture thread ID from JSON output
2. `uwf thread list` — verify the thread appears in the list
3. `uwf thread show <threadId>` — verify head pointer exists
4. `uwf thread exec <threadId> --agent uwf-builtin` — execute one step
5. Verify exec returns JSON with a head field
Report results. Pass threadId and containerName forward.
output: "Report test results. Set $status to pass (with threadId, workflowName, containerName) or fail."
frontmatter:
oneOf:
- properties:
$status: { const: "pass" }
threadId: { type: string }
workflowName: { type: string }
containerName: { type: string }
required: [$status, threadId, workflowName, containerName]
- properties:
$status: { const: "fail" }
error: { type: string }
containerName: { type: string }
required: [$status, error, containerName]
inspect:
description: "Test step list/show, thread read, and CAS operations"
goal: "You are an E2E test runner. Validate read and inspect operations inside the Docker container."
capabilities:
- docker
- shell
procedure: |
Use the container (containerName) and threadId from your prompt.
All commands via: `docker exec <containerName> bash -c '...'`
Set env: PATH="$HOME/.bun/bin:$PATH" UWF_HOME=/tmp/uwf-e2e-storage
Step inspection:
1. `uwf step list <threadId>` — verify steps array has length > 1
2. Capture the last step hash from the output
3. `uwf step show <lastStepHash>` — verify it returns a role field
Thread read:
4. `uwf thread read <threadId>` — verify non-empty output
CAS operations:
5. `ocas get <lastStepHash>` — verify returns a type field
6. `ocas has <lastStepHash>` — verify exits 0
7. `ocas refs <lastStepHash>` — list refs (may be empty)
8. `ocas walk <lastStepHash>` — verify returns non-empty array
Report results. Pass threadId, lastStepHash, workflowName, containerName forward.
output: "Report test results. Set $status to pass (with threadId, lastStepHash, workflowName, containerName) or fail."
frontmatter:
oneOf:
- properties:
$status: { const: "pass" }
threadId: { type: string }
lastStepHash: { type: string }
workflowName: { type: string }
containerName: { type: string }
required: [$status, threadId, lastStepHash, workflowName, containerName]
- properties:
$status: { const: "fail" }
error: { type: string }
containerName: { type: string }
required: [$status, error, containerName]
cancel-and-fork:
description: "Test thread cancel, step fork, and log inspection"
goal: "You are an E2E test runner. Validate cancel, fork, and log operations inside the Docker container."
capabilities:
- docker
- shell
procedure: |
Use containerName, threadId, lastStepHash, and workflowName from your prompt.
All commands via: `docker exec <containerName> bash -c '...'`
Set env: PATH="$HOME/.bun/bin:$PATH" UWF_HOME=/tmp/uwf-e2e-storage
Cancel:
1. Start a second thread: `uwf thread start <workflowName> -p 'E2E cancel test'`
2. Cancel it: `uwf thread cancel <secondThreadId>`
3. Verify it appears in cancelled list: `uwf thread list --status cancelled`
Fork:
4. Fork from the first thread's last step: `uwf step fork <lastStepHash>`
5. Verify fork creates a new thread with a different ID
Logs:
6. `uwf log list` — verify output (may be empty)
7. `uwf log show --thread <threadId>` — verify runs without error
Report results with summary.
output: "Report test results with summary. Set $status to pass or fail."
frontmatter:
oneOf:
- properties:
$status: { const: "pass" }
containerName: { type: string }
summary: { type: string }
required: [$status, containerName, summary]
- properties:
$status: { const: "fail" }
error: { type: string }
containerName: { type: string }
required: [$status, error, containerName]
cleanup:
description: "Remove Docker container"
goal: "You are an E2E test runner. Clean up the Docker container used for testing."
capabilities:
- docker
- shell
procedure: |
Remove the Docker container (containerName is in your prompt):
1. `docker rm -f <containerName>`
2. Verify the container is gone: `docker ps -a --filter name=<containerName> --format '{{.Names}}'` should return empty
Report cleanup result.
output: "Report cleanup result. Set $status to pass or fail."
frontmatter:
oneOf:
- properties:
$status: { const: "pass" }
summary: { type: string }
required: [$status, summary]
- properties:
$status: { const: "fail" }
error: { type: string }
required: [$status, error]
graph:
$START:
new: { role: "bootstrap", prompt: "Set up the Docker container and verify uwf is runnable." }
resume: { role: "bootstrap", prompt: "Review the previous run output and continue the walkthrough." }
bootstrap:
pass: { role: "config-and-registry", prompt: "Container {{{containerName}}} is ready. Validate config and workflow registration." }
fail: { role: "$END", prompt: "Bootstrap failed: {{{error}}}. No container was created." }
config-and-registry:
pass: { role: "thread-ops", prompt: "Config and registry OK. Workflow '{{{workflowName}}}' registered. Container: {{{containerName}}}. Now test thread operations." }
fail: { role: "cleanup", prompt: "Config/registry failed: {{{error}}}. Clean up container {{{containerName}}}." }
thread-ops:
pass: { role: "inspect", prompt: "Thread ops OK. threadId={{{threadId}}}, workflowName={{{workflowName}}}, containerName={{{containerName}}}. Now test inspect operations." }
fail: { role: "cleanup", prompt: "Thread ops failed: {{{error}}}. Clean up container {{{containerName}}}." }
inspect:
pass: { role: "cancel-and-fork", prompt: "Inspect OK. threadId={{{threadId}}}, lastStepHash={{{lastStepHash}}}, workflowName={{{workflowName}}}, containerName={{{containerName}}}. Now test cancel, fork, and logs." }
fail: { role: "cleanup", prompt: "Inspect failed: {{{error}}}. Clean up container {{{containerName}}}." }
cancel-and-fork:
pass: { role: "cleanup", prompt: "All tests passed! {{{summary}}}. Clean up container {{{containerName}}}." }
fail: { role: "cleanup", prompt: "Cancel/fork failed: {{{error}}}. Clean up container {{{containerName}}}." }
cleanup:
pass: { role: "$END", prompt: "E2E walkthrough complete. {{{summary}}}" }
fail: { role: "$END", prompt: "Cleanup failed: {{{error}}}. Manual cleanup may be needed." }
@@ -0,0 +1,855 @@
name: normalize-bun-monorepo
graph:
ci:
done:
role: solve-issue-workflow
prompt: CI configured. Register solve-issue workflow for repo at {{{repoPath}}}.
skipped:
role: solve-issue-workflow
prompt: "ci already configured, skipped."
failed:
role: solve-issue-workflow
prompt: CI setup failed ({{{reason}}}), but continue. Register solve-issue workflow for repo at {{{repoPath}}}.
biome:
done:
role: package-metadata
prompt: Biome configured. Standardize package metadata for repo at {{{repoPath}}}.
skipped:
role: package-metadata
prompt: "biome already configured, skipped."
failed:
role: package-metadata
prompt: Biome setup failed ({{{reason}}}), but continue. Standardize package metadata for repo at {{{repoPath}}}.
$START:
new:
role: workspace
prompt: Set up bun workspace structure for repo at {{{repoPath}}}.
resume:
role: workspace
prompt: Review the previous run output and continue setting up the bun workspace structure for repo at {{{repoPath}}}.
release:
done:
role: testing
prompt: Release pipeline configured. Set up vitest for repo at {{{repoPath}}}.
skipped:
role: testing
prompt: "release already configured, skipped."
failed:
role: testing
prompt: Release pipeline failed ({{{reason}}}), but continue. Set up vitest for repo at {{{repoPath}}}.
testing:
done:
role: ci
prompt: Testing configured. Set up Gitea CI for repo at {{{repoPath}}}.
skipped:
role: ci
prompt: "testing already configured, skipped."
failed:
role: ci
prompt: Testing setup failed ({{{reason}}}), but continue. Set up Gitea CI for repo at {{{repoPath}}}.
committer:
failed:
role: $END
prompt: "Commit failed: {{{reason}}}."
committed:
role: $END
prompt: "Normalization committed: {{{commitHash}}}."
no_changes:
role: $END
prompt: Repo already normalized, no changes needed.
workspace:
done:
role: typescript
prompt: Workspace ready. Configure TypeScript for repo at {{{repoPath}}}.
skipped:
role: typescript
prompt: "workspace already configured, skipped."
failed:
role: typescript
prompt: Workspace setup failed ({{{reason}}}), but continue. Configure TypeScript for repo at {{{repoPath}}}.
guardrails:
done:
role: committer
prompt: All normalization complete. Commit changes in repo at {{{repoPath}}}.
skipped:
role: committer
prompt: "guardrails already configured, skipped."
failed:
role: committer
prompt: Guardrails failed ({{{reason}}}), but commit whatever was done in repo at {{{repoPath}}}.
typescript:
done:
role: biome
prompt: TypeScript configured. Set up Biome for repo at {{{repoPath}}}.
skipped:
role: biome
prompt: "typescript already configured, skipped."
failed:
role: biome
prompt: TypeScript setup failed ({{{reason}}}), but continue. Set up Biome for repo at {{{repoPath}}}.
package-metadata:
done:
role: release
prompt: Package metadata standardized. Configure release pipeline for repo at {{{repoPath}}}.
skipped:
role: release
prompt: "package-metadata already configured, skipped."
failed:
role: release
prompt: Package metadata failed ({{{reason}}}), but continue. Configure release pipeline for repo at {{{repoPath}}}.
solve-issue-workflow:
done:
role: guardrails
prompt: Solve-issue workflow placed in .workflows/. Install guardrails for repo at {{{repoPath}}}.
skipped:
role: guardrails
prompt: "solve-issue-workflow already configured, skipped."
failed:
role: guardrails
prompt: Solve-issue workflow failed ({{{reason}}}), but continue. Install guardrails for repo at {{{repoPath}}}.
roles:
ci:
goal: You configure Gitea Actions CI for build, lint, and test on push/PR.
output: Describe the CI pipeline configured. Set $status to done or failed.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
If `.gitea/workflows/ci.yml` already exists, review it for completeness but don't overwrite unless it's missing key steps.
If `.github/workflows/` exists (GitHub Actions), keep it — add `.gitea/workflows/` alongside it.
Create `.gitea/workflows/ci.yml` (if not present):
```yaml
name: CI
on:
push:
branches: ['*']
pull_request:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- name: Build
run: bun run build
- name: Lint
run: bun run check
- name: Test
run: bun run test:ci
```
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# 1. CI file exists
test -f .gitea/workflows/ci.yml
# 2. YAML is valid
node -e "
const fs = require('fs');
const content = fs.readFileSync('.gitea/workflows/ci.yml', 'utf8');
if (!content.includes('bun')) { console.error('Missing bun setup'); process.exit(1); }
if (!content.includes('test')) { console.error('Missing test step'); process.exit(1); }
console.log('CI YAML looks valid');
"
# 3. Required root scripts exist for CI to work
node -e "
const pkg = require('./package.json');
const required = ['build', 'check', 'test:ci'];
const missing = required.filter(s => !pkg.scripts?.[s]);
if (missing.length) { console.error('Missing scripts for CI:', missing.join(', ')); process.exit(1); }
console.log('All CI-required scripts present');
"
```
Post-condition: CI file exists, YAML references bun and test, all required scripts exist in package.json.
description: Set up Gitea CI workflow
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- ci-config
biome:
goal: You configure Biome for consistent code quality across the monorepo.
output: List what was configured and any remaining lint issues. Set $status to done or failed.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
Be idempotent — if biome.json already exists, merge missing settings rather than overwriting.
Check and fix:
1. Install biome: add `@biomejs/biome` to root devDependencies (skip if already present)
2. Root `biome.json` must exist with at minimum:
- `files.includes`: `["**", "!**/dist", "!**/node_modules"]`
- `formatter`: indentStyle space, indentWidth 2, lineWidth 100
- `javascript.formatter`: quoteStyle double, semicolons always
- `linter.rules.nursery.noConsole: "error"` (production code)
- Override for test files (`**/__tests__/**`): `noConsole: "off"`, `noExplicitAny: "off"`
- `assist.actions.source.organizeImports: "on"`
If biome.json already exists, only add missing fields — preserve existing customizations.
3. Root scripts must include: `"check"` (should include `biome check .`), `"format": "biome format --write ."`
4. Run `bunx biome check .` — fix auto-fixable issues with `bunx biome check . --fix`
5. Remaining unfixable issues: list them but don't block
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# 1. biome.json exists
test -f biome.json
# 2. biome is installed
bunx biome --version
# 3. check runs (exit 0 or list remaining issues)
bunx biome check . 2>&1 || true
```
Post-condition: `biome.json` exists, `bunx biome check .` runs (may have warnings but no infrastructure errors).
description: Configure Biome linter and formatter
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- linter-config
release:
goal: "You set up the complete release pipeline: changesets for version management, publish script for npm release."
output: Describe what was configured. Set $status to done or failed.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
Be idempotent — skip steps that are already done.
## Part 1: Changesets
1. Install: add `@changesets/cli` to root devDependencies (skip if present), run `bun install`
2. If `.changeset/config.json` does not exist, run `bunx changeset init`
3. Verify `.changeset/config.json` has:
- `"access": "public"` (for @scoped packages)
- `"baseBranch": "main"`
4. Add `.changeset/README.md` if missing
## Part 2: Publish Script
5. Create `scripts/publish-all.mjs` (skip if already exists and looks correct):
- Scan `packages/` for non-private packages (skip `"private": true`)
- Determine publish order by resolving workspace dependency graph (dependees before dependents)
- For each package in order:
a. Replace `"workspace:^"` deps with actual versions from workspace
b. Run `npm publish --access public --ignore-scripts` (MUST use --ignore-scripts to bypass prepublishOnly guardrail)
c. Restore original package.json
- Support flags: `--dry-run`, `--tag <name>`
- Handle errors: if one package fails, restore and continue
CRITICAL: The publish command MUST include `--ignore-scripts` because the guardrails role adds a `prepublishOnly` script that blocks direct publishing.
## Part 3: Root Scripts
6. Root scripts must include:
- `"changeset": "bunx changeset"`
- `"version": "bunx changeset version"`
- `"release": "bun run build && bun run test && node scripts/publish-all.mjs"`
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# 1. changeset config exists and is valid
test -f .changeset/config.json
node -e "const c = require('./.changeset/config.json'); console.log('access:', c.access, 'baseBranch:', c.baseBranch)"
# 2. changeset status works
bunx changeset status 2>&1 || true
# 3. publish script exists and uses --ignore-scripts
test -f scripts/publish-all.mjs
grep -q 'ignore-scripts' scripts/publish-all.mjs
# 4. dry run works
node scripts/publish-all.mjs --dry-run
```
Post-condition: All verification commands pass.
description: Configure changesets and publish pipeline
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- changeset-config
- release-config
testing:
goal: You set up vitest test infrastructure across the monorepo.
output: List what was configured per package. Set $status to done or failed.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
Be idempotent — do NOT overwrite existing vitest.config.ts or test files.
Check and fix:
1. Add `vitest` to root devDependencies (skip if present), run `bun install`
2. For each package under `packages/`:
- If `vitest.config.ts` does NOT already exist, create it:
```ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
include: ["src/__tests__/**/*.test.ts"],
},
});
```
- If package.json has no `"test"` script, add: `"test": "vitest run --passWithNoTests"`, `"test:ci": "vitest run --passWithNoTests"`
- Create `src/__tests__/` directory if it doesn't exist
3. Root package.json scripts must include:
- `"test": "bun run --filter './packages/*' test"`
- `"test:ci": "bun run --filter './packages/*' test:ci"`
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# 1. vitest is installed
bunx vitest --version
# 2. each package has vitest config
for d in packages/*/; do
if [ -f "$d/vitest.config.ts" ]; then echo "$d: ✅"; else echo "$d: ❌ missing vitest.config.ts"; fi
done
# 3. root test script works
bun run test 2>&1 || true
```
Post-condition: `bun run test` runs without infrastructure errors (no tests is OK, test failures are OK).
description: Configure vitest for all packages
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- test-config
committer:
goal: You commit all the changes made by previous roles in a single clean commit.
output: List files changed and commit hash. Set $status to committed or no_changes.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
1. Review all changes: `git diff --stat` and `git status`
2. Verify key files exist from previous roles (spot-check):
```bash
echo "=== Spot-check ==="
if [ -f tsconfig.json ]; then echo "✅ tsconfig.json"; else echo "ℹ️ tsconfig.json (skipped for JS/MJS)"; fi
for f in .gitignore biome.json .changeset/config.json .gitea/workflows/ci.yml .githooks/pre-push; do
test -f "$f" && echo "✅ $f" || echo "⚠️ MISSING: $f"
done
node -e "
const p = require('./package.json');
const required = ['build', 'check', 'test', 'test:ci', 'format', 'preinstall', 'prepublishOnly'];
const missing = required.filter(s => !p.scripts?.[s]);
if (missing.length) console.log('⚠️ Missing scripts:', missing.join(', '));
else console.log('✅ All scripts present');
if (!p.packageManager) console.log('⚠️ Missing packageManager');
else console.log('✅ packageManager:', p.packageManager);
"
```
List any missing items as warnings but still commit what exists.
3. If no changes: set $status=no_changes
4. Stage all: `git add -A`
5. **Before committing, check for build artifacts that should NOT be committed:**
```bash
# Detect compiled output accidentally staged
git diff --cached --name-only | grep -E '\.(d\.ts|\.js\.map)$' | grep -v node_modules | head -20
# Also check for .js files next to .ts sources (build output in src/)
for f in $(git diff --cached --name-only | grep -E '\.js$' | grep -v node_modules | grep -v scripts/); do
ts_file="${f%.js}.ts"
if [ -f "$ts_file" ]; then echo "BUILD ARTIFACT: $f (has matching $ts_file)"; fi
done
```
If build artifacts are found:
- Unstage them: `git reset HEAD <files>`
- Add patterns to `.gitignore` if missing (e.g. `*.d.ts`, `*.js.map`, or specific output dirs)
- Re-run `git add -A` after updating `.gitignore`
6. Commit: `git commit -m "chore: normalize to bun monorepo conventions"`
7. Push: `git push`
Post-condition: Clean commit pushed, `git status` shows clean working tree. No build artifacts in the commit.
description: Commits all normalization changes
frontmatter:
oneOf:
- properties:
$status:
const: committed
commitHash:
type: string
repoPath:
type: string
- properties:
$status:
const: no_changes
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities: []
workspace:
goal: You set up the foundational bun workspace configuration for a monorepo.
output: List what was changed. Set $status to done (workspace working) or failed (with reason).
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path provided in your task prompt.
Be idempotent — check before modifying. If something is already correct, skip it.
Check and fix:
1. Root `package.json` must have `"workspaces": ["packages/*"]`
2. Root `package.json` must have `"private": true`
3. If packages exist in other locations (e.g. root src/, top-level dirs), migrate them under `packages/`
4. Each package under `packages/` must have its own `package.json` with `"name"` and `"type": "module"`
5. `.gitignore` must exist and include at minimum:
```
node_modules/
dist/
*.tsbuildinfo
```
If `.gitignore` is missing or doesn't cover these, append the missing entries (don't overwrite existing content).
6. If node_modules/ is already tracked in git, remove it: `git rm -r --cached node_modules/ */node_modules/ 2>/dev/null`
7. Run `bun install` to verify workspace resolution works
## Verification (must all pass)
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# 1. bun install works
bun install
# 2. gitignore covers essentials
grep -q 'node_modules' .gitignore
grep -q 'dist' .gitignore
# 3. no node_modules tracked
test -z "$(git ls-files | grep node_modules)"
# 4. all packages have package.json
for d in packages/*/; do test -f "$d/package.json" || echo "MISSING: $d/package.json"; done
```
Post-condition: All verification commands pass.
description: Ensure bun workspace structure
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- workspace-setup
guardrails:
goal: You configure enforcement mechanisms that block npm/pnpm/yarn usage and direct npm publish.
output: List what guardrails were installed. Set $status to done or failed.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
Be idempotent — check before adding.
## 1. Block wrong package manager
Add to root `package.json` (if not already present):
- `"packageManager": "bun@<version>"` — use the version from `bun --version`
- `"scripts.preinstall": "npx only-allow bun"` — blocks npm/pnpm/yarn install
## 2. Block direct npm publish
Add to root `package.json` (if not already present):
- `"scripts.prepublishOnly": "echo 'Use bun run release instead' && exit 1"`
For each non-private package under `packages/`:
- Add `"scripts.prepublishOnly": "echo 'Use bun run release from repo root' && exit 1"` to their package.json (if not present)
If `scripts/publish-all.mjs` exists, verify it uses `--ignore-scripts` in the npm publish command.
If it doesn't, add `--ignore-scripts` to the publish command.
## 3. Git hooks
Create `.githooks/pre-push` (if not already present):
```bash
#!/usr/bin/env bash
set -euo pipefail
echo "🔍 Running checks..."
bun run check
echo "🧪 Running tests..."
bun run test
echo "✅ All checks passed!"
```
Make it executable: `chmod +x .githooks/pre-push`
Configure git to use hooks dir: `git config core.hooksPath .githooks`
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# 1. packageManager field exists
node -e "const p = require('./package.json'); if (!p.packageManager) { console.error('❌ missing packageManager'); process.exit(1); } console.log('✅ packageManager:', p.packageManager)"
# 2. preinstall guard exists
node -e "const p = require('./package.json'); if (!p.scripts?.preinstall?.includes('only-allow')) { console.error('❌ missing preinstall guard'); process.exit(1); } console.log('✅ preinstall guard')"
# 3. npm install is blocked (with timeout to prevent hang)
timeout 10 npm install 2>&1 | head -5 || true
# 4. prepublishOnly exists
node -e "const p = require('./package.json'); if (!p.scripts?.prepublishOnly) { console.error('❌ missing prepublishOnly'); process.exit(1); } console.log('✅ prepublishOnly guard')"
# 5. publish script uses --ignore-scripts
if [ -f scripts/publish-all.mjs ]; then grep -q 'ignore-scripts' scripts/publish-all.mjs && echo '✅ publish uses --ignore-scripts' || echo '❌ publish missing --ignore-scripts'; fi
# 6. git hooks configured
test -f .githooks/pre-push && echo '✅ pre-push hook' || echo '❌ missing pre-push hook'
```
Post-condition: All verification checks pass.
description: Install project guardrails to prevent wrong package manager and publish workflow
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- guardrails
typescript:
goal: You configure TypeScript for a bun monorepo with composite project references.
output: List what was configured. Set $status to done or failed.
procedure: |-
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
Be idempotent — if tsconfig.json already exists with correct settings, don't overwrite.
## Step 0: Detect if project needs TypeScript compilation
```bash
TS_FILES=$(find packages/ -name '*.ts' -o -name '*.tsx' | grep -v node_modules | grep -v dist | grep -v '.d.ts' | head -5)
```
**If there are NO .ts/.tsx source files** (pure JS/MJS project):
- Do NOT create root tsconfig.json
- Do NOT add `bunx tsc --build` as the build script
- Do NOT add typescript/bun-types to devDependencies unless already present
- Preserve the existing `build` script (e.g. vite build, esbuild, etc.)
- If no build script exists, add one based on the project's bundler (check for vite.config, esbuild, etc.)
- Set $status=done with note "JS/MJS project — skipped TypeScript setup"
- STOP HERE.
**If .ts/.tsx files exist**, continue with full TypeScript setup:
Check and fix:
1. Root `tsconfig.json` must exist with:
- `"compilerOptions"`: target ES2022, module NodeNext, moduleResolution NodeNext, strict true, composite true, declaration true, declarationMap true, sourceMap true
- `"references"`: array with `{ "path": "packages/<name>" }` for each package
- `"files": []` (root does not compile files directly)
2. Each package must have `tsconfig.json` with:
- `"extends": "../../tsconfig.json"` (inherit root config)
- `"compilerOptions": { "rootDir": "src", "outDir": "dist" }`
- `"include": ["src"]`
- `"references"` to sibling packages it depends on
3. Root scripts must include: `"build": "bunx tsc --build"`, `"typecheck": "bunx tsc --build"`
4. `devDependencies` at root: `typescript`, `bun-types`, `@types/node`
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
if [ ! -f tsconfig.json ]; then
echo "JS/MJS project — no tsconfig needed"
node -e "const p = require('./package.json'); if (!p.scripts?.build) { console.error('Missing build script'); process.exit(1); } console.log('build:', p.scripts.build)"
bun run build
exit 0
fi
test -f tsconfig.json
for d in packages/*/; do test -f "$d/tsconfig.json" || echo "MISSING: $d/tsconfig.json"; done
bunx tsc --build
```
Post-condition: For TS projects — `bunx tsc --build` succeeds. For JS/MJS projects — `bun run build` succeeds.
description: Configure TypeScript with project references
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- typescript-config
package-metadata:
goal: You ensure every package has consistent metadata for publishing and discoverability.
output: List what was standardized per package. Set $status to done or failed.
procedure: |
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
Be idempotent — skip fields that are already correctly set.
For each package under `packages/`:
1. `"type": "module"` — must be set
2. `"files": ["src", "dist", "package.json"]` — for published packages (non-private)
3. `"publishConfig": { "access": "public" }` — for @scoped public packages (non-private)
4. `"repository"` — must point to the correct git remote and directory
- Read remote URL: `git remote get-url origin`
- Set `"directory": "packages/<name>"`
5. `"exports"` — conditional exports for TypeScript packages:
```json
".": {
"bun": "./src/index.ts",
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
```
Skip if package has no `src/index.ts`.
6. Private packages (server, frontend, tools) must have `"private": true` and can skip publishConfig/files
7. Each package should have a `"scripts"` section with at least `"test"` if tests exist
8. Workspace dependencies should use `"workspace:^"` protocol, not version numbers
- Check: `grep -r '"@united-workforce/' packages/*/package.json | grep -v 'workspace:'`
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
# Check every non-private package has required fields
for d in packages/*/; do
pkg="$d/package.json"
test -f "$pkg" || continue
node -e "
const p = require('./$pkg');
if (p.private) { console.log('$d: private, skip'); process.exit(0); }
const issues = [];
if (p.type !== 'module') issues.push('missing type:module');
if (!p.exports) issues.push('missing exports');
if (!p.publishConfig) issues.push('missing publishConfig');
if (!p.files) issues.push('missing files');
if (issues.length) console.log('$d:', issues.join(', '));
else console.log('$d: OK');
"
done
# No hardcoded workspace deps
! grep -r '"@united-workforce/' packages/*/package.json | grep -v 'workspace:' | grep -v node_modules
```
Post-condition: Verification script shows OK for all non-private packages, no hardcoded workspace versions.
description: Standardize package.json metadata across all packages
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- package-config
solve-issue-workflow:
goal: You place a solve-issue workflow YAML in .workflows/ so the project can use uwf thread start .workflows/solve-issue.yaml for issue resolution.
output: Describe the workflow registered. Set $status to done or failed.
procedure: |-
## GROUND RULES (read before doing anything)
- Only report actions you ACTUALLY performed and files you ACTUALLY created or modified.
- If everything is already correctly configured, set $status=skipped.
- NEVER fabricate command output. Run every verification command for real and paste the actual stdout/stderr.
- After writing a file, run `test -f <path> && echo EXISTS || echo MISSING` to confirm it was actually written.
cd into the repo path from your task prompt.
1. Check if `uwf` CLI is available: `which uwf`
- If not available: set $status=failed, reason="uwf CLI not installed"
2. Check if `.workflows/solve-issue.yaml` already exists:
- If it exists and looks correct (has planner/developer/reviewer/tester/committer roles), skip. Set $status=done.
3. Create `.workflows/solve-issue.yaml` adapted for this project:
- Copy the standard solve-issue workflow structure (planner -> developer -> reviewer -> tester -> committer)
- Adjust the developer role procedure to use this project's test runner and build commands
- The workflow should reference the correct repo path and build toolchain (bun)
NOTE: Place the file in `.workflows/` (dot-prefix), NOT `workflows/`. Do NOT run `uwf workflow add`. The file is used directly via `uwf thread start .workflows/solve-issue.yaml`.
## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
```bash
test -f .workflows/solve-issue.yaml
node -e "
const fs = require('fs');
const content = fs.readFileSync('.workflows/solve-issue.yaml', 'utf8');
const required = ['planner', 'developer', 'reviewer', 'tester', 'committer'];
const missing = required.filter(r => !content.includes(r + ':'));
if (missing.length) { console.error('Missing roles:', missing.join(', ')); process.exit(1); }
console.log('All roles present');
"
```
Post-condition: `.workflows/solve-issue.yaml` exists with all 5 roles.
description: Register solve-issue workflow for the project
frontmatter:
oneOf:
- properties:
$status:
const: done
repoPath:
type: string
- properties:
$status:
const: skipped
repoPath:
type: string
- properties:
$status:
const: failed
reason:
type: string
repoPath:
type: string
capabilities:
- workflow-config
description: Normalize an existing project to @united-workforce bun monorepo conventions. Supports both TypeScript and JS/MJS projects. Each role handles one configuration layer. All roles allow fail.