Merge pull request #10 from ProjectOpenSea/ux/tone-and-bootstrap-polish #20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: validate | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Validate manifest.json | |
| run: | | |
| set -euo pipefail | |
| jq . manifest.json > /dev/null | |
| # Required top-level keys + types | |
| jq -e '."$schema" == "https://agents.pinata.cloud/schemas/manifest.v1.json"' manifest.json > /dev/null | |
| jq -e '.version == 1' manifest.json > /dev/null | |
| jq -e '.agent | type == "object"' manifest.json > /dev/null | |
| jq -e '.template | type == "object"' manifest.json > /dev/null | |
| jq -e '.skills | type == "array"' manifest.json > /dev/null | |
| jq -e '.scripts | type == "object"' manifest.json > /dev/null | |
| jq -e '.routes | type == "array"' manifest.json > /dev/null | |
| # Agent shape | |
| jq -e '.agent.name | type == "string" and length > 0' manifest.json > /dev/null | |
| jq -e '.agent.description | type == "string" and length > 0' manifest.json > /dev/null | |
| jq -e '.agent.vibe | type == "string"' manifest.json > /dev/null | |
| jq -e '.agent.emoji | type == "string"' manifest.json > /dev/null | |
| # Template shape — note: schema doesn't define template.version | |
| jq -e '.template.slug | type == "string" and test("^[a-z0-9-]+$")' manifest.json > /dev/null | |
| jq -e '.template.category | type == "string"' manifest.json > /dev/null | |
| jq -e '.template.tags | type == "array" and length > 0 and length <= 10' manifest.json > /dev/null | |
| jq -e '.template.authorName | type == "string"' manifest.json > /dev/null | |
| # Skills shape — every entry needs `name` plus EITHER `clawhub_slug` (resolves to latest published version) OR `cid` (pinned). | |
| jq -e 'all(.skills[]; .name | type == "string" and length > 0)' manifest.json > /dev/null | |
| jq -e 'all(.skills[]; | |
| ((.clawhub_slug // "") | test("^[a-z0-9-]+$")) | |
| or ((.cid // "") | type == "string" and length >= 46) | |
| )' manifest.json > /dev/null | |
| # Scripts shape — build is required (start is optional) | |
| jq -e '.scripts.build | type == "string" and length > 0' manifest.json > /dev/null | |
| # Secrets shape — every entry needs name + description; required defaults to true | |
| jq -e '.secrets | type == "array" and length > 0' manifest.json > /dev/null | |
| jq -e 'all(.secrets[]; .name | type == "string" and test("^[A-Z][A-Z0-9_]*$"))' manifest.json > /dev/null | |
| jq -e 'all(.secrets[]; .description | type == "string" and length > 0)' manifest.json > /dev/null | |
| # Required secrets — only the irreducible ones. The agent auto-provisions the rest at bootstrap | |
| # (OPENSEA_API_KEY via the OpenSea instant-key endpoint; PRIVY_WALLET_ID via `opensea wallet create`; | |
| # PRIVY_AUTH_SIGNING_KEY via `opensea wallet generate-auth-key`), then attaches them via the bundled | |
| # @pinata/platform skill. App credentials still have to come from the user. | |
| for v in PRIVY_APP_ID PRIVY_APP_SECRET; do | |
| jq -e --arg v "$v" 'any(.secrets[]; .name == $v and .required == true)' manifest.json > /dev/null \ | |
| || { echo "missing required secret: $v"; exit 1; } | |
| done | |
| # Auto-provisioned secrets must be declared (with required: false) so Pinata renders the field | |
| # in the env UI for users who want to override the auto-provisioned default. | |
| for v in OPENSEA_API_KEY PRIVY_WALLET_ID PRIVY_AUTH_SIGNING_KEY; do | |
| jq -e --arg v "$v" 'any(.secrets[]; .name == $v)' manifest.json > /dev/null \ | |
| || { echo "missing optional secret declaration: $v"; exit 1; } | |
| done | |
| echo "manifest.json OK" | |
| - name: Validate .openclaw/openclaw.json | |
| run: | | |
| set -euo pipefail | |
| jq . .openclaw/openclaw.json > /dev/null | |
| jq -e '.compaction | type == "string"' .openclaw/openclaw.json > /dev/null | |
| jq -e '.maxConcurrent | type == "number"' .openclaw/openclaw.json > /dev/null | |
| echo ".openclaw/openclaw.json OK" | |
| - name: Check required workspace files exist | |
| run: | | |
| set -euo pipefail | |
| required=( | |
| ".openclaw/SOUL.md" | |
| "workspace/SOUL.md" | |
| "workspace/AGENTS.md" | |
| "workspace/IDENTITY.md" | |
| "workspace/TOOLS.md" | |
| "workspace/BOOTSTRAP.md" | |
| "workspace/HEARTBEAT.md" | |
| "workspace/USER.md" | |
| "README.md" | |
| "LICENSE" | |
| ) | |
| missing=0 | |
| for f in "${required[@]}"; do | |
| if [ ! -f "$f" ]; then | |
| echo "missing: $f" | |
| missing=1 | |
| fi | |
| done | |
| [ "$missing" -eq 0 ] | |
| echo "workspace files OK" | |
| - name: Lint markdown — no broken in-repo links | |
| run: python3 .github/scripts/check_links.py |