diff --git a/.github/workflows/queen-deploy.yml b/.github/workflows/queen-deploy.yml index 76e4791e96..c46dad3719 100644 --- a/.github/workflows/queen-deploy.yml +++ b/.github/workflows/queen-deploy.yml @@ -1,72 +1,60 @@ -name: Queen Self-Improving Container +name: Queen — build check + +# Railway deploys the Queen itself, from its own GitHub hook, the same way +# woody-weed-bot ships. This workflow therefore does NOT deploy: it only proves +# the artefact Railway is about to build actually builds. +# +# It replaces a workflow that pushed a Docker image and called a Railway +# GraphQL mutation. That version ran 35 times between 2026-04-01 and +# 2026-04-03 and succeeded zero times — 32 failures and 3 cancellations, every +# one of them on the same "Build and Push to Railway" step. It could not have +# worked: it pushed to a registry without ever running `docker login`, it +# carried a placeholder project id (90c3f0c8-…-5e123abc4567) instead of the +# real one (e6dfacdc-3eed-48ae-929b-02e87f07f0aa), its `test` job read +# `steps.deploy.outputs` from a different job, and on every run it posted +# "✅ Queen deployed" to issue 476 — a claim that was never once true. +# +# Railway собирает Королеву сам, по своему GitHub-хуку. Этот workflow ничего +# не деплоит — он только доказывает, что артефакт действительно собирается. on: push: branches: [main] - paths: [apps/queen-web/**, src/tri/queen/**, deploy/Dockerfile.queen] + paths: + - 'apps/queen-web/**' + - '.github/workflows/queen-deploy.yml' + pull_request: + paths: + - 'apps/queen-web/**' workflow_dispatch: -env: - RAILWAY_PROJECT_ID: 90c3f0c8-c26e-4c77-9e45-5e123abc4567 - jobs: - deploy: + build: runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Configure Railway - run: | - echo "RAILWAY_API_TOKEN=${{ secrets.RAILWAY_TOKEN }}" >> $GITHUB_ENV - echo "RAILWAY_PROJECT_ID=${{ env.RAILWAY_PROJECT_ID }}" >> $GITHUB_ENV - - - name: Build and Push to Railway - run: | - # Build Queen container image - docker build -f deploy/Dockerfile.queen -t registry.${{ env.RAILWAY_PROJECT_ID }}.railway.app/queen:latest . - - # Push to Railway registry - docker push registry.${{ env.RAILWAY_PROJECT_ID }}.railway.app/queen:latest + defaults: + run: + working-directory: apps/queen-web - - name: Deploy to Railway - run: | - # Trigger Railway deployment via API - curl -X POST \ - -H "Authorization: Bearer ${{ secrets.RAILWAY_TOKEN }}" \ - -H "Content-Type: application/json" \ - https://backboard.railway.app/graphql/v2 \ - -d '{ - "query": "mutation { deployService(input: { projectId: \"'"$RAILWAY_PROJECT_ID"'\", serviceName: \"queen\" }) { id } }" - }' + steps: + - uses: actions/checkout@v4 - - name: Get Deployment URL - run: | - # Get service URL from Railway - SERVICE_URL=$(curl -s -X POST \ - -H "Authorization: Bearer ${{ secrets.RAILWAY_TOKEN }}" \ - -H "Content-Type: application/json" \ - https://backboard.railway.app/graphql/v2 \ - -d '{ - "query": "query { project(id: \"'"$RAILWAY_PROJECT_ID"'\") { services { name url } } }" - }' | jq -r '.data.project.services[] | select(.name == "queen") | .url') + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: apps/queen-web/package-lock.json - echo "Queen deployed to: $SERVICE_URL" - echo "queen_url=$SERVICE_URL" >> $GITHUB_OUTPUT + - name: Install + run: npm ci - - name: Comment on Issue - if: github.event_name == 'push' - run: | - gh issue comment 476 --body "✅ Queen deployed to ${{ steps.deploy.outputs.queen_url }}" + - name: Build + run: npm run build - test: - runs-on: ubuntu-latest - needs: deploy - steps: - - name: Test Health Endpoint + # A build that emits nothing still exits 0. Assert the artefact exists, + # so this check cannot go green on an empty dist. + - name: Assert the bundle was actually emitted run: | - # Wait for deployment to be ready - sleep 30 - - # Test health endpoint - curl -f ${{ steps.deploy.outputs.queen_url }}/health || exit 1 + test -f dist/index.html || { echo "dist/index.html missing"; exit 1; } + test -n "$(find dist/assets -name '*.js' -print -quit)" || { echo "no JS bundle emitted"; exit 1; } + echo "emitted:" + ls -la dist dist/assets diff --git a/.gitignore b/.gitignore index 7849bb1246..7190a23287 100644 --- a/.gitignore +++ b/.gitignore @@ -316,6 +316,11 @@ fpga/tools/weight_pack_wide !CLAUDE.md !.github/ !docs/** +# Queen Trinity Orchestrator Law: every container MUST carry SOUL.md at +# its root. The *.md rule above made that impossible to honour -- only +# templates/SOUL.md was re-included, so a container's own soul could never +# be committed without -f. +!**/SOUL.md # Docker files in root (use deploy/ subdirectory instead) Dockerfile* diff --git a/apps/queen-web/SOUL.md b/apps/queen-web/SOUL.md new file mode 100644 index 0000000000..3ced0f119c --- /dev/null +++ b/apps/queen-web/SOUL.md @@ -0,0 +1,84 @@ +# SOUL.md — Agent Soul Binding + +**Law**: Every container/agent MUST have `SOUL.md` at root. + +--- + +## Agent Identity + +| Field | Value | +|-------|--------| +| **Agent Type** | Custom — Queen web surface (no autonomous loop) | +| **Agent ID** | `queen-web` | +| **Bound Issue** | `#476` | + +--- + +## Mission + +```markdown +Serve the Queen dashboard as a static surface on Railway. + +This container carries the React UI only. It executes nothing on the user's +behalf, holds no credentials, and makes no autonomous decisions — so the +8-step agent cycle, the MNL pattern and Akashic journaling do not apply to it. +It is bound to issue #476 because that is the issue the Queen deployment has +always been reported against. +``` + +--- + +## Allowed Commands + +```markdown +npm ci — install from the committed lockfile +npm run build — emit dist/ +npm start — vite preview --host 0.0.0.0 --port $PORT + +Nothing else. This container has no shell entrypoint and no write access to +the Trinity tree. +``` + +--- + +## Stop Conditions + +```markdown +- The build emits no dist/index.html or no JS bundle → fail, do not deploy. +- The process binds anything other than 0.0.0.0:$PORT → fail; Railway routes + to $PORT and a loopback bind is unreachable from outside the container. +``` + +--- + +## Reporting Format + +Protocol v2, on issue #476. + +`✅ [DONE]` may only be posted after the deployed URL has answered. The +workflow this replaced posted "✅ Queen deployed" on every run, including all +32 failures — the claim was never once verified against a live URL. + +--- + +## Scope — what this is NOT + +The Queen's Zig backend is **not** in this container and does not currently +exist as a buildable artefact: + +- `build.zig` declares 67 steps; none is named `queen-backend`, which + `deploy/Dockerfile.queen` invokes. The closest is `libqueen`, and it builds + the `libtrinity-queen` C API library for the SwiftUI dashboard, not a server. +- `src/server_main.zig` is a 48-line stub that answers every request with the + same JSON, is wired into no build step, and binds `127.0.0.1`. + +The brain (`src/brain`) therefore has no vehicle yet either: it is Zig, and it +would ship inside that backend once the backend exists. + +--- + +## References + +- `CLAUDE.md` — Queen Trinity Orchestrator Law +- `AGENTS.md` +- `.claude/rules/no-shell-scripts.md` — why the entrypoint is not `sh -c` diff --git a/apps/queen-web/package.json b/apps/queen-web/package.json index 23df47b672..c45f62fdc2 100644 --- a/apps/queen-web/package.json +++ b/apps/queen-web/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "start": "vite preview --host 0.0.0.0 --port ${PORT:-8080}" }, "dependencies": { "react": "^19.0.0", diff --git a/apps/queen-web/railway.json b/apps/queen-web/railway.json new file mode 100644 index 0000000000..90040c707d --- /dev/null +++ b/apps/queen-web/railway.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://railway.com/railway.schema.json", + "build": { + "builder": "NIXPACKS", + "buildCommand": "npm ci && npm run build" + }, + "deploy": { + "startCommand": "npm start", + "healthcheckPath": "/", + "healthcheckTimeout": 30, + "restartPolicyType": "ON_FAILURE", + "restartPolicyMaxRetries": 3 + } +}