Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 46 additions & 58 deletions .github/workflows/queen-deploy.yml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down
84 changes: 84 additions & 0 deletions apps/queen-web/SOUL.md
Original file line number Diff line number Diff line change
@@ -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`
3 changes: 2 additions & 1 deletion apps/queen-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
14 changes: 14 additions & 0 deletions apps/queen-web/railway.json
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading