diff --git a/.github/workflows/hardening-validation.yml b/.github/workflows/hardening-validation.yml index e25aefd51..005fac6b4 100644 --- a/.github/workflows/hardening-validation.yml +++ b/.github/workflows/hardening-validation.yml @@ -7,12 +7,16 @@ on: - 'pmoves/docker-compose*.yml' - 'pmoves/services/**/Dockerfile*' - '.github/workflows/hardening-validation.yml' + - '.github/workflows/integrations-ghcr.matrix.json' + - 'pmoves/integrations/archon' pull_request: branches: [main, PMOVES.AI-Edition-Hardened-Integrations, PMOVES.AI-Edition-Hardened] paths: - 'pmoves/docker-compose*.yml' - 'pmoves/services/**/Dockerfile*' - '.github/workflows/hardening-validation.yml' + - '.github/workflows/integrations-ghcr.matrix.json' + - 'pmoves/integrations/archon' workflow_dispatch: concurrency: @@ -400,6 +404,13 @@ jobs: echo "✓ pmoves/monitoring/docker-compose.monitoring.yml is valid" fi + - name: Validate GHCR matrix paths + run: | + python3 pmoves/tools/ghcr_local_prepublish.py \ + --matrix-file .github/workflows/integrations-ghcr.matrix.json \ + --repo-root . \ + --paths-only + - name: Check for network segmentation run: | echo "Checking network segmentation..." diff --git a/.github/workflows/sync-secrets-local.yml b/.github/workflows/sync-secrets-local.yml index af07e71c0..476a7710a 100644 --- a/.github/workflows/sync-secrets-local.yml +++ b/.github/workflows/sync-secrets-local.yml @@ -347,6 +347,7 @@ jobs: if-no-files-found: warn - name: Summary + shell: bash run: | # Validate that matrix.target only contains safe label characters. # matrix.target is a workflow_dispatch string input — it's template-interpolated @@ -366,9 +367,15 @@ jobs: echo " source pmoves/scripts/bootstrap_credentials.sh" if [ -f "pmoves/data/chit/env.cgp.json" ]; then echo "" - echo "Pattern B pull (non-runner nodes, e.g. Z890/4090):" + echo "Pattern B pull (non-runner nodes):" + echo " # Unix/Linux:" + echo " gh run download ${{ github.run_id }} --repo POWERFULMOVES/PMOVES.AI \\" + echo " --name chit-bundle-$_target-${{ github.run_id }} \\" + echo " --dir \"\${XDG_CONFIG_HOME:-\$HOME/.config}/pmoves/chit\"" + echo " # Windows (4090):" echo " gh run download ${{ github.run_id }} --repo POWERFULMOVES/PMOVES.AI \\" echo " --name chit-bundle-$_target-${{ github.run_id }} \\" - echo " --dir \"\$XDG_CONFIG_HOME/pmoves/chit\"" - echo " make -C pmoves secrets-funnel-sync" + echo " --dir \"\$APPDATA/pmoves/chit\"" + echo " # Then materialize (preserves bundle — does NOT overwrite with local keys):" + echo " make -C pmoves secrets-funnel-sync-from-bundle" fi diff --git a/pmoves/Makefile b/pmoves/Makefile index 22b8e26e0..8e8b06f8c 100644 --- a/pmoves/Makefile +++ b/pmoves/Makefile @@ -397,7 +397,7 @@ supa-start: ensure-env-shared ## Start Supabase stack for selected runtime (cli -e KONG_PG_PASSWORD=$${SUPABASE_DB_PASSWORD:-postgres} \ kong:3.7.1 kong migrations bootstrap --yes >/dev/null 2>&1 || true; \ $(MAKE) --no-print-directory supa-bootstrap-db; \ - $(DC) up -d --force-recreate supabase-postgrest supabase-gotrue supabase-kong supabase-realtime supabase-storage supabase-studio supabase-imgproxy supabase-meta supabase-edge-functions supabase-analytics supabase-vector supabase-pooler; \ + $(DC) --profile supabase-local up -d --force-recreate supabase-postgrest supabase-gotrue supabase-kong supabase-realtime supabase-storage supabase-studio supabase-imgproxy supabase-meta supabase-edge-functions supabase-analytics supabase-vector supabase-pooler; \ fi @$(MAKE) --no-print-directory supa-status @$(MAKE) --no-print-directory supa-collation-refresh 2>/dev/null || true @@ -423,6 +423,12 @@ supa-restart: ## Restart Supabase stack for selected runtime @sleep 2 @$(MAKE) --no-print-directory supa-start +.PHONY: pooler-recreate +pooler-recreate: ## Force-recreate supabase-pooler container to pick up env file changes (non-disruptive) + @echo "→ Force-recreating supabase-pooler with current env..." + @$(DC) --profile supabase-local up -d --force-recreate supabase-pooler + @echo "✓ pooler recreated — give it 30s for health check" + supa-status: ## Show Supabase status for selected runtime and export env snapshot when available @echo "🔍 Supabase status (SUPABASE_RUNTIME=$(SUPABASE_RUNTIME)):" @if [ "$(SUPABASE_RUNTIME)" = "cli" ]; then \ @@ -637,6 +643,50 @@ supabase-bootstrap: ## Apply pending Supabase migrations + seeds (tracked in pub fi @echo "✅ Supabase bootstrap complete" +.PHONY: supabase-bootstrap-no-start +supabase-bootstrap-no-start: ## Apply migrations+seeds against already-running DB (skips supa-start; uses SUPABASE_DB_USER) + @echo "🗄️ Applying Supabase schema + seeds (no-start mode)..." + @set -euo pipefail; \ + db=$$(bash "$(SUPABASE_DB_HELPER)" running); \ + if [ -z "$$db" ]; then echo "❌ Supabase DB container not found — start it first"; exit 1; fi; \ + pg_user=$$(docker exec "$$db" sh -c 'echo $${POSTGRES_USER:-postgres}'); \ + echo " DB container: $$db | pg_user: $$pg_user"; \ + _psql() { docker exec -i "$$db" psql -U "$$pg_user" -h 127.0.0.1 -d postgres "$$@"; }; \ + _psql -v ON_ERROR_STOP=1 -c "CREATE TABLE IF NOT EXISTS public.pmoves_bootstrap_history (kind text NOT NULL, filename text NOT NULL, applied_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (kind, filename));"; \ + if [ -d "supabase/migrations" ]; then \ + for migration in $$(find supabase/migrations -maxdepth 1 -type f -name '*.sql' | LC_ALL=C sort); do \ + [ -f "$$migration" ] || continue; \ + name=$$(basename "$$migration"); \ + applied=$$(_psql -tAc "SELECT 1 FROM public.pmoves_bootstrap_history WHERE kind='migration' AND filename='$$name' LIMIT 1;" | tr -d '[:space:]'); \ + if [ "$$applied" = "1" ]; then \ + echo " SKIP (applied): $$name"; \ + continue; \ + fi; \ + echo " APPLY: $$name"; \ + _psql < "$$migration" && \ + _psql -c "INSERT INTO public.pmoves_bootstrap_history(kind,filename) VALUES('migration','$$name') ON CONFLICT DO NOTHING;" || \ + echo " WARN: $$name failed — continuing"; \ + done; \ + fi; \ + if [ -d "supabase/initdb" ]; then \ + for seed in $$(find supabase/initdb -maxdepth 1 -type f -name '*.sql' | LC_ALL=C sort); do \ + [ -f "$$seed" ] || continue; \ + name=$$(basename "$$seed"); \ + applied=$$(_psql -tAc "SELECT 1 FROM public.pmoves_bootstrap_history WHERE kind='seed' AND filename='$$name' LIMIT 1;" | tr -d '[:space:]'); \ + if [ "$$applied" = "1" ]; then \ + echo " SKIP (applied): $$name"; \ + continue; \ + fi; \ + echo " APPLY seed: $$name"; \ + _psql < "$$seed" && \ + _psql -c "INSERT INTO public.pmoves_bootstrap_history(kind,filename) VALUES('seed','$$name') ON CONFLICT DO NOTHING;" || \ + echo " WARN: seed $$name failed — continuing"; \ + done; \ + else \ + echo " ⚠️ No initdb directory found"; \ + fi + @echo "✅ Supabase bootstrap (no-start) complete" + supabase-bootstrap-mark-applied: ## Mark all migration/seed files as applied in bootstrap history (no SQL execution) @echo "📝 Marking migrations/seeds as applied in public.pmoves_bootstrap_history..." @$(MAKE) --no-print-directory supa-start >/dev/null @@ -1433,6 +1483,74 @@ bootstrap-tier-envs: ## Create missing tier env files from examples (Hardened br echo "✓ All tier env files already exist."; \ fi +.PHONY: tier-data-patch-missing +tier-data-patch-missing: ## Append compose-required keys missing from env.tier-data (idempotent) + @if [ ! -f env.tier-data ]; then echo "env.tier-data not found"; exit 1; fi; \ + changed=0; \ + for key in QDRANT__API_KEY NEO4J_PASSWORD; do \ + if ! grep -q "^$$key=" env.tier-data 2>/dev/null; then \ + val=$$(openssl rand -hex 32); \ + echo "$$key=$$val" >> env.tier-data; \ + echo "→ Appended $$key to env.tier-data"; \ + changed=1; \ + fi; \ + done; \ + if ! grep -q "^MINIO_USER=" env.tier-data 2>/dev/null; then \ + echo "MINIO_USER=minioadmin" >> env.tier-data; \ + echo "→ Appended MINIO_USER to env.tier-data"; \ + changed=1; \ + fi; \ + if ! grep -q "^MINIO_PASSWORD=" env.tier-data 2>/dev/null; then \ + val=$$(openssl rand -base64 24 | tr -d '\n='); \ + echo "MINIO_PASSWORD=$$val" >> env.tier-data; \ + echo "→ Appended MINIO_PASSWORD to env.tier-data"; \ + changed=1; \ + fi; \ + if ! grep -q "^WGER_DB_PASSWORD=" env.tier-data 2>/dev/null; then \ + val=$$(openssl rand -base64 24 | tr -d '\n='); \ + echo "WGER_DB_PASSWORD=$$val" >> env.tier-data; \ + echo "→ Appended WGER_DB_PASSWORD to env.tier-data"; \ + changed=1; \ + fi; \ + if [ "$$changed" = "0" ]; then echo "✓ env.tier-data already has all required keys"; fi + +.PHONY: tier-api-patch-missing +tier-api-patch-missing: ## Append compose-required keys missing from env.tier-api (idempotent) + @if [ ! -f env.tier-api ]; then echo "env.tier-api not found"; exit 1; fi; \ + changed=0; \ + for key in WGER_SECRET_KEY WGER_ADMIN_PASSWORD MCP_SERVER_TOKEN; do \ + if ! grep -q "^$$key=" env.tier-api 2>/dev/null; then \ + val=$$(openssl rand -base64 32 | tr -d '\n='); \ + echo "$$key=$$val" >> env.tier-api; \ + echo "→ Appended $$key to env.tier-api"; \ + changed=1; \ + fi; \ + done; \ + if [ "$$changed" = "0" ]; then echo "✓ env.tier-api already has all required keys"; fi + +.PHONY: tier-supabase-patch-missing +tier-supabase-patch-missing: ## Append/fix compose-required keys in env.tier-supabase (idempotent) + @if [ ! -f env.tier-supabase ]; then echo "env.tier-supabase not found"; exit 1; fi; \ + changed=0; \ + for key in SECRET_KEY_BASE LOGFLARE_PUBLIC_ACCESS_TOKEN LOGFLARE_PRIVATE_ACCESS_TOKEN; do \ + if ! grep -q "^$$key=" env.tier-supabase 2>/dev/null; then \ + val=$$(openssl rand -base64 48 | tr -d '\n=' | cut -c1-64); \ + echo "$$key=$$val" >> env.tier-supabase; \ + echo "→ Appended $$key to env.tier-supabase"; \ + changed=1; \ + fi; \ + done; \ + _vault_val=$$(grep "^VAULT_ENC_KEY=" env.tier-supabase 2>/dev/null | cut -d= -f2-); \ + if [ -z "$$_vault_val" ]; then \ + echo "VAULT_ENC_KEY=$$(openssl rand -hex 16)" >> env.tier-supabase; \ + echo "→ Appended VAULT_ENC_KEY (32-char hex)"; changed=1; \ + elif [ "$${#_vault_val}" -ne 32 ]; then \ + _new_vault=$$(openssl rand -hex 16); \ + sed -i "s|^VAULT_ENC_KEY=.*|VAULT_ENC_KEY=$$_new_vault|" env.tier-supabase; \ + echo "→ Fixed VAULT_ENC_KEY (was $${#_vault_val} chars, now 32)"; changed=1; \ + fi; \ + if [ "$$changed" = "0" ]; then echo "✓ env.tier-supabase already has all required keys"; fi + .PHONY: populate-tier-envs populate-tier-envs: bootstrap-tier-envs ## Populate tier env files with defaults from env.shared and .env.local @echo "Populating tier env files with defaults..." @@ -1749,6 +1867,110 @@ integration-contract-check-strict: ## Strict integration contract check (missing integration-contract-check-baseline: ## Strict contract checks for template + baseline overlays @$(PYTHON) tools/integration_contract_check.py $(INTEGRATION_CONTRACT_BASELINE) --strict-hooks +CF_PAGES_PROJECT ?= cataclysmstudios +CF_PAGES_DIR ?= ../deploy/cataclysmstudios +CF_PAGES_BRANCH ?= main + +CF_PAGES_PORT ?= 8788 + +.PHONY: cf-pages-dev cf-pages-create cf-pages-deploy cf-pages-status +cf-pages-dev: ## Serve cataclysmstudios locally via wrangler pages dev (http://localhost:CF_PAGES_PORT) + @echo "→ Previewing $(CF_PAGES_PROJECT) at http://localhost:$(CF_PAGES_PORT)" + npx wrangler pages dev "$(CF_PAGES_DIR)" --port $(CF_PAGES_PORT) +cf-pages-create: ensure-env-shared ## Create Cloudflare Pages project (one-time bootstrap) + @$(LOAD_ENV_SHARED); \ + if [ -z "$${CLOUDFLARE_API_TOKEN:-}" ]; then \ + echo "❌ CLOUDFLARE_API_TOKEN not set — run: make secrets-funnel-sync-from-bundle"; \ + exit 1; \ + fi; \ + if [ -z "$${CLOUDFLARE_ACCOUNT_ID:-}" ]; then \ + echo "❌ CLOUDFLARE_ACCOUNT_ID not set — run: make secrets-funnel-sync-from-bundle"; \ + exit 1; \ + fi; \ + echo "→ Creating Cloudflare Pages project: $(CF_PAGES_PROJECT)"; \ + CLOUDFLARE_API_TOKEN="$${CLOUDFLARE_API_TOKEN}" \ + CLOUDFLARE_ACCOUNT_ID="$${CLOUDFLARE_ACCOUNT_ID}" \ + npx wrangler pages project create "$(CF_PAGES_PROJECT)" \ + --production-branch main 2>&1; \ + echo "✅ CF Pages project created — now run: make cf-pages-deploy" + +cf-pages-deploy: ensure-env-shared ## Deploy cataclysmstudios static site to Cloudflare Pages + @$(LOAD_ENV_SHARED); \ + if [ -z "$${CLOUDFLARE_API_TOKEN:-}" ]; then \ + echo "❌ CLOUDFLARE_API_TOKEN not set — run: make secrets-funnel-sync"; \ + exit 1; \ + fi; \ + if [ -z "$${CLOUDFLARE_ACCOUNT_ID:-}" ]; then \ + echo "❌ CLOUDFLARE_ACCOUNT_ID not set — run: make secrets-funnel-sync"; \ + exit 1; \ + fi; \ + echo "→ Deploying $(CF_PAGES_PROJECT) to Cloudflare Pages..."; \ + CLOUDFLARE_API_TOKEN="$${CLOUDFLARE_API_TOKEN}" \ + CLOUDFLARE_ACCOUNT_ID="$${CLOUDFLARE_ACCOUNT_ID}" \ + npx wrangler pages deploy "$(CF_PAGES_DIR)" \ + --project-name "$(CF_PAGES_PROJECT)" \ + --branch "$(CF_PAGES_BRANCH)" \ + --commit-dirty=true 2>&1; \ + echo "✅ CF Pages deploy complete" + +cf-pages-status: ensure-env-shared ## Check Cloudflare Pages project status + @$(LOAD_ENV_SHARED); \ + CLOUDFLARE_API_TOKEN="$${CLOUDFLARE_API_TOKEN}" \ + CLOUDFLARE_ACCOUNT_ID="$${CLOUDFLARE_ACCOUNT_ID}" \ + npx wrangler pages project list 2>&1 | grep -i "$(CF_PAGES_PROJECT)" || echo "(project not found)" + +PMOVES_AI_PROJECT ?= pmoves-ai +PMOVES_AI_DIR ?= ../website +PMOVES_AI_BRANCH ?= main +PMOVES_AI_PORT ?= 8789 + +.PHONY: pmoves-ai-dev pmoves-ai-create pmoves-ai-deploy pmoves-ai-status +pmoves-ai-dev: ## Serve pmoves.ai locally via wrangler pages dev (http://localhost:PMOVES_AI_PORT) + @echo "→ Previewing $(PMOVES_AI_PROJECT) at http://localhost:$(PMOVES_AI_PORT)" + npx wrangler pages dev "$(PMOVES_AI_DIR)" --port $(PMOVES_AI_PORT) + +pmoves-ai-create: ensure-env-shared ## Create pmoves-ai Cloudflare Pages project (one-time bootstrap) + @$(LOAD_ENV_SHARED); \ + if [ -z "$${CLOUDFLARE_API_TOKEN:-}" ]; then \ + echo "❌ CLOUDFLARE_API_TOKEN not set — run: make secrets-funnel-sync-from-bundle"; \ + exit 1; \ + fi; \ + if [ -z "$${CLOUDFLARE_ACCOUNT_ID:-}" ]; then \ + echo "❌ CLOUDFLARE_ACCOUNT_ID not set — run: make secrets-funnel-sync-from-bundle"; \ + exit 1; \ + fi; \ + echo "→ Creating Cloudflare Pages project: $(PMOVES_AI_PROJECT)"; \ + CLOUDFLARE_API_TOKEN="$${CLOUDFLARE_API_TOKEN}" \ + CLOUDFLARE_ACCOUNT_ID="$${CLOUDFLARE_ACCOUNT_ID}" \ + npx wrangler pages project create "$(PMOVES_AI_PROJECT)" \ + --production-branch main 2>&1; \ + echo "✅ pmoves-ai Pages project created — now run: make pmoves-ai-deploy" + +pmoves-ai-deploy: ensure-env-shared ## Deploy pmoves.ai static site to Cloudflare Pages + @$(LOAD_ENV_SHARED); \ + if [ -z "$${CLOUDFLARE_API_TOKEN:-}" ]; then \ + echo "❌ CLOUDFLARE_API_TOKEN not set — run: make secrets-funnel-sync-from-bundle"; \ + exit 1; \ + fi; \ + if [ -z "$${CLOUDFLARE_ACCOUNT_ID:-}" ]; then \ + echo "❌ CLOUDFLARE_ACCOUNT_ID not set — run: make secrets-funnel-sync-from-bundle"; \ + exit 1; \ + fi; \ + echo "→ Deploying $(PMOVES_AI_PROJECT) to Cloudflare Pages (branch: $(PMOVES_AI_BRANCH))..."; \ + CLOUDFLARE_API_TOKEN="$${CLOUDFLARE_API_TOKEN}" \ + CLOUDFLARE_ACCOUNT_ID="$${CLOUDFLARE_ACCOUNT_ID}" \ + npx wrangler pages deploy "$(PMOVES_AI_DIR)" \ + --project-name "$(PMOVES_AI_PROJECT)" \ + --branch "$(PMOVES_AI_BRANCH)" \ + --commit-dirty=true 2>&1; \ + echo "✅ pmoves-ai deploy complete" + +pmoves-ai-status: ensure-env-shared ## Check pmoves-ai Cloudflare Pages project status + @$(LOAD_ENV_SHARED); \ + CLOUDFLARE_API_TOKEN="$${CLOUDFLARE_API_TOKEN}" \ + CLOUDFLARE_ACCOUNT_ID="$${CLOUDFLARE_ACCOUNT_ID}" \ + npx wrangler pages project list 2>&1 | grep -i "$(PMOVES_AI_PROJECT)" || echo "(pmoves-ai project not found)" + up-cloudflare: ensure-env-shared ## Start the Cloudflare tunnel connector (profile=cloudflare) @$(LOAD_ENV_SHARED); \ if [ -z "$$CLOUDFLARE_TUNNEL_TOKEN" ] && [ -z "$$CLOUDFLARE_TUNNEL_NAME" ]; then \ @@ -2716,6 +2938,13 @@ ghcr-dispatch-one: ci-runners-check-strict ## Dispatch GHCR workflow for a singl ghcr-list: ## List all images in the integrations-ghcr matrix @$(PYTHON) -c "import json,pathlib;[print(e['name']) for e in json.loads(pathlib.Path('$(GHCR_MATRIX_FILE)').read_text())]" +.PHONY: ghcr-matrix-paths-check +ghcr-matrix-paths-check: ## Validate all in-repo GHCR matrix context/dockerfile paths exist (no Docker build) + @$(PYTHON) $(GHCR_PREPUBLISH_SCRIPT) \ + --matrix-file "$(GHCR_MATRIX_FILE)" \ + --repo-root .. \ + --paths-only + .PHONY: deepresearch-smoke deepresearch-smoke: ## Publish a sample DeepResearch request and wait for a result (requires worker running) @ENV_FILE="$(CURDIR)/env.shared" bash -lc '. ./scripts/with-env.sh "$$ENV_FILE" && PYTHONPATH="$(CURDIR)/.." python3 tools/deepresearch_smoke.py --timeout $${DEEPRESEARCH_SMOKE_TIMEOUT:-60}' diff --git a/pmoves/docs/AGENTS/AGNOTE4482.md b/pmoves/docs/AGENTS/AGNOTE4482.md index b584089e4..b34bb7f73 100644 --- a/pmoves/docs/AGENTS/AGNOTE4482.md +++ b/pmoves/docs/AGENTS/AGNOTE4482.md @@ -1161,3 +1161,36 @@ No open PR existed for `codex/big-ball-5090-gap-closure` at the time of this not - Timestamp: `2026-05-26` + +## Antigravity CLI A2UI Hologram Scaling Fix (2026-05-30) + +### Context +Operator requested fixing the "identical pink dot" gallery issue stemming from the pending `A2UI Remotion hologram viewport scaling (1920x1080 viewport)` ticket. The `geometry_mesh` element in `a2ui-renderer` was stubbed out and required DGX SPARK physical access. + +This work ran on the local workspace `3fd5d899-f774-45da-bae1-ef349bf01951` targeting branch `fix/ghcr-matrix-paths-gate` in the parent PMOVES repo. + +### Work Performed +- Bypassed the stubbed Remotion 2D generator and implemented a live interactive 3D WebGL solution on the landing page. +- Upgraded `website/hyperdim/index.html` with URL parameter parsing (`?preset=` and `?ui=none`) for headless preset embedding. +- Generated three distinct parametric topology presets derived from `beats_constellation.json`: + - `beats_c5.json`: Allegro Balanced Bright (High tempo, bright color) + - `beats_c3.json`: Allegro Balanced Deep (Moderate tempo, deep color) + - `beats_c1.json`: Cluster 1 (High fitness, tight curvature, very bright) +- Replaced the three static `