Skip to content

fix(ui): resolve black canvas webgl issues and link darkxside school playlist - #1677

Closed
POWERFULMOVES wants to merge 12 commits into
mainfrom
fix/ghcr-matrix-paths-gate
Closed

POWERFULMOVES wants to merge 12 commits into
mainfrom
fix/ghcr-matrix-paths-gate

Conversation

@POWERFULMOVES

@POWERFULMOVES POWERFULMOVES commented Jun 1, 2026

Copy link
Copy Markdown
Owner

This PR resolves the black WebGL canvas issues in the default and embed viewers for hyperdimensions. It ensures the default nautilus fallback evaluates the surface function, and forces the Three.js renderer to recalculate geometry when the sidebar collapses in ui=none mode. It also replaces the TODO with the requested YouTube playlist link for DARKXSIDE'S School.

Summary by CodeRabbit

  • New Features

    • Interactive 3D parametric surface visualization app (Hyperdimensions) with 30+ presets including torus, Klein bottle, Mandelbulb, and beat-driven surfaces.
    • Website gallery section showcasing pipeline outputs with embedded viewer.
  • Documentation

    • Added system audit trail entry for hologram scaling fix.
  • Style

    • CSS updates for new gallery layout and sidebar UI components.

POWERFULMOVES and others added 12 commits May 27, 2026 14:22
Adds a --paths-only mode to ghcr_local_prepublish.py that checks all
in-repo matrix context/dockerfile paths exist without running Docker.
Submodule paths under pmoves/integrations/ are initialized shallowly
before the check. Exits non-zero with a per-entry PASS/FAIL table.

Wires the check into hardening-validation.yml (required pre-merge gate)
and adds .github/workflows/integrations-ghcr.matrix.json to trigger
paths so the gate fires whenever the matrix is edited.

Adds make ghcr-matrix-paths-check for local dev use.

This gate would have caught the archon-ui-main/ staleness (PR #1635)
before it reached CI. Depends on #1635 to pass on main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nk bump

If a PR only bumps the Archon submodule gitlink without editing the
matrix JSON, the path gate wouldn't fire. Add pmoves/integrations/archon
to the hardening-validation trigger so gitlink-only bumps still run the
check. Uses the specific gitlink path rather than integrations/** to
avoid firing on unrelated routine submodule bumps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reject absolute paths upfront and verify that resolved context/dockerfile
paths are still under repo_root after resolution. Prevents ../- or
absolute-path entries in the matrix JSON from silently passing the
existence check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…start target

- find_db_container.sh: prefer pmoves-supabase-db-1 over legacy DoX
  supabase-db container (two-pass grep to fix docker ps ordering issue)
- Makefile: add supabase-bootstrap-no-start target that applies
  migrations+seeds against an already-running DB, bypassing supa-start;
  detects POSTGRES_USER from container env, uses -h 127.0.0.1 TCP auth
- Makefile: supa-start line 400 now uses --profile supabase-local when
  force-recreating the pooler, preventing name-conflict on supabase-pooler
- Makefile: add tier-data/api/supabase-patch-missing targets (idempotent
  key append for compose :? required vars) and pooler-recreate target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Summary step used bash [[ ]] syntax without `shell: bash`, causing
PowerShell parse failures on the Windows 4090 self-hosted runner. The
main sync step (which has shell: bash) succeeded and uploaded the
artifact correctly, but the cosmetic Summary step crashed the job.

Also: update Pattern B pull instructions to show both Unix + Windows
paths and reference secrets-funnel-sync-from-bundle (which preserves
the CI bundle instead of overwriting it from local env.shared).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…studios

Three new Makefile targets for Cloudflare Pages lifecycle management:
- cf-pages-create: one-time project bootstrap (production-branch=main)
- cf-pages-deploy: wrangler pages deploy with CF_PAGES_BRANCH=main default
- cf-pages-status: list projects to verify state

CF_PAGES_BRANCH Make variable (default: main) ensures deploys always land
on the production URL (cataclysmstudios.pages.dev) not a branch preview.
Credentials flow through LOAD_ENV_SHARED from the CHIT-materialized tier files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Serves deploy/cataclysmstudios/ at localhost:8788 (configurable via
CF_PAGES_PORT) using wrangler pages dev before committing to a prod deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- website/wrangler.toml: name=pmoves-ai, compatibility_date=2026-05-28
- pmoves/Makefile: pmoves-ai-dev/create/deploy/status targets with
  PMOVES_AI_PROJECT/DIR/BRANCH/PORT Make variables
- Deployed to https://pmoves-ai.pages.dev (production branch: main)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s.ai

- website/assets/gallery/: 3 hologram MP4s (C1/C3/C5) from beats_to_cgp
  pipeline + CHIT hero/mindmap images from session gallery
- website/hyperdim/: full Hyperdimensions Three.js viewer with all 28 saves
  including beats_constellation.json + chit_manifold.json PMOVES presets
- website/index.html: new #gallery section with autoplay looping video cards
  and pipeline note; Demos section Hyperdimensions card (badge-live);
  Gallery nav link added
- website/styles.css: .gallery-grid, .gallery-item, .gallery-pipeline-note,
  .badge-live styles

Live at https://pmoves-ai.pages.dev

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ds to fix 1920x1080 scaling

- Bypassed the stubbed Remotion 2D generator which required DGX SPARK.

- Upgraded website/hyperdim/index.html with URL parameter parsing.

- Generated three distinct parametric topology presets derived from beats_constellation.json.

- Replaced the three static <video> elements in website/index.html gallery with <iframe> embeds.

- Appended ANTIGRAVITY-GEMINI claim and ACK to AGNOTE4482.md.
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR delivers a comprehensive interactive 3D visualization web application (Hyperdimensions), strengthens infrastructure CI/CD with GHCR path validation gates, and improves build/deployment automation. It adds 30+ parametric surface presets, integrates the viewer into the main website gallery, and updates workflow/make targets for secrets synchronization and Supabase operations.

Changes

Infrastructure Hardening and CI/CD

Layer / File(s) Summary
GHCR matrix path validation system
pmoves/tools/ghcr_local_prepublish.py, .github/workflows/hardening-validation.yml, pmoves/Makefile
Adds --paths-only CLI flag to validate integration context/dockerfile paths exist and are safe without building. Updates hardening-validation workflow to trigger on matrix/archon changes and run the new validation step. Adds ghcr-matrix-paths-check make target.
Secrets synchronization and bundle handling
.github/workflows/sync-secrets-local.yml, pmoves/mk/codex.mk
Updates Pattern B workflow section to differentiate Unix/Linux vs Windows destination paths. Adds secrets-funnel-sync-from-bundle make target to read CHIT bundle directly, validate presence, and run secrets_sync.py without re-exporting.
Supabase pooler and bootstrap improvements
pmoves/Makefile, pmoves/scripts/supabase/find_db_container.sh
Supabase pooler now uses --profile supabase-local. Adds pooler-recreate to force-recreate the pooler for env changes. Adds supabase-bootstrap-no-start to apply migrations/seeds to already-running DB without starting compose, tracking applied files and continuing on failures. Updates container lookup to prefer PMOVES naming.
Tier environment patching and Cloudflare automation
pmoves/Makefile
Adds tier-data-patch-missing, tier-api-patch-missing, tier-supabase-patch-missing targets to verify/repair env.tier-* files. Adds cf-pages-* (cataclysmstudios) and pmoves-ai-* (pmoves.ai) targets for dev/create/deploy/status via wrangler.

Hyperdimensional Visualization Web App

Layer / File(s) Summary
Hyperdimensional viewer application
website/hyperdim/index.html
Complete single-page app (2172 lines) with Three.js 3D renderer, CodeMirror editor, parametric surface visualization, camera controls (WASD panning, double-click tweening, auto-rotate), parameter animation playback, preset loading/saving, JSON configuration persistence, and URL query parameter support (ui=none, preset=...).
Surface preset library
website/hyperdim/saves/(_list.json, *.json)
30+ parametric surface presets: beat-driven constellations (beats_c1/c3/c5, beats_constellation), mathematical surfaces (Hopf fibration, Klein bottle, Weierstrass, Fourier, MLP, perceptron, mandelbulb, ocean planet, planet, rose, shells, torus knot, trefoil knot), and procedural shapes (donut, hotdog, jellyfish, breather, dini, möbius, nautilus, sphere tutorial, taylor, torus, hypersphere, cosmic bloom, chit manifold). Each includes display settings, tunable parameters, embedded surface code, and output conversion flags.
App styling and configuration
website/hyperdim/styles.css, website/wrangler.toml
Dark flex-based stylesheet (541 lines) for sidebar, resizable viewport, collapsible sections, input/button styling, CodeMirror integration, and parameter controls. Wrangler config for pmoves-ai Pages project.

Main Website Gallery Integration

Layer / File(s) Summary
Website gallery section and responsive styling
website/index.html, website/styles.css
Adds "PIPELINE GALLERY" section with Hyperdimensions iframe, gallery items, and descriptive captions. Updates "Live demos" to feature Hyperdimensions as live. Adds gallery navigation link and updates Cloudflare School playlist URL. Implements responsive gallery grid CSS with video aspect ratio, item cards, captions, and pipeline-note container.

Documentation

Layer / File(s) Summary
Audit trail
pmoves/docs/AGENTS/AGNOTE4482.md
New AGNOTE4482 entry documenting 2026-05-30 A2UI hologram scaling fix: WebGL/Three.js solution replacing stubbed Remotion, website updates (preset parameters, gallery integration), beat-derived topology presets, and agent ACK.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Possibly related PRs


Suggested labels

documentation, enhancement, python


Suggested reviewers

  • hunnibear

Poem

🐰 A dimensional rabbit hops through space,
Three.js meshes render at quickened pace,
Beat topologies bloom in spectral light,
CI gates hold firm, validating right,
From Hopf to Mandelbulb, wonders rise—
A gallery of math before your eyes!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description lacks required sections from the template (Testing, Required Checks, Review Coordination, Follow-up Tasks). While it provides a brief summary of changes, it does not follow the specified template structure. Add Testing section with commands/output, check the Required Checks checkboxes, include Review Coordination section, and add Follow-up Tasks section to match the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main changes: resolving black canvas WebGL issues and linking the DARKXSIDE school playlist, which aligns with the changeset's core objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ghcr-matrix-paths-gate
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/ghcr-matrix-paths-gate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d34ae948ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -0,0 +1,46 @@
{
"surfaceFn": "function surface(input) {\n const u = (input.u - 0.5) * 10;\n const v = (input.v - 0.5) * 10;\n const eps = input.epsilon || 0.0;\n \n const z = Math.sin(u)*Math.cos(v) * eps;\n \n return {\n x: u, \n y: v, \n z: z, \n r: 0.5, \n g: 0.5, \n b: 0.5, \n a: 0.8\n };\n}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Convert chit manifold preset to the current save schema

This preset is saved with legacy keys (surfaceFn, params, animatedParams), but the new Hyperdimensions loader reads cfg.surface.code, cfg.parameters, and cfg.extraParameters before falling back to the default placeholder surface. Because the landing page advertises chit_manifold, selecting it will silently ignore the intended manifold function/epsilon animation and render the default surface instead of the CHIT manifold.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/sync-secrets-local.yml (1)

360-367: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Recompute or persist CGP_FILE, ENV_FILE, and PY before printing the summary.

These names are shell locals from the previous step, so this step prints empty paths and an unusable decode command. The sync can succeed while the operator instructions are still wrong.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/sync-secrets-local.yml around lines 360 - 367, The summary
echo prints empty CGP_FILE, ENV_FILE, and PY because those shell locals are out
of scope in this step; update the workflow step to recompute or import/persist
these variables before the echoes (e.g., re-source the script that sets them or
read them from prior step outputs/exports into the environment) so that
CGP_FILE, ENV_FILE and PY contain the correct values when printing the summary
and when showing the PMOVES_CGP_FILE decode command.
website/index.html (1)

317-328: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

The two school cards now route to the same playlist.

Lines 317 and 328 point to the identical YouTube playlist even though the cards describe different schools. Unless both brands intentionally share one playlist, the Cloudflare CTA now sends users to the wrong curriculum.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/index.html` around lines 317 - 328, The two article elements with
class "card card-school" both use the same YouTube playlist href; update the
href on the incorrect anchor so each school card points to its intended
playlist. Locate the <article class="card card-school"> that contains the
heading text for the other school (e.g., "CLOUDFLARE'S School of ...") and
change its <a class="btn btn-ghost" href="..."> href to the correct YouTube
playlist URL for that school, keeping rel="noopener noreferrer" and
target="_blank" unchanged; then verify the two anchors now point to two distinct
playlist URLs.
🟡 Minor comments (10)
pmoves/docs/AGENTS/AGNOTE4482.md-1181-1186 (1)

1181-1186: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Unrelated handoff item in Lane Status.

The "Custom Domain Linking" row (line 1185) mentions manual Cloudflare domain configuration, which is not described in the Context, Work Performed, or PR objectives. If this is unrelated to the A2UI hologram scaling fix work, it should be removed from this audit entry or explained in the Work Performed section.

Also: Fix table formatting

Static analysis flags missing blank lines around the table (MD058):

 
 ### Lane Status
+
 | Lane | Status | Notes |
 |------|--------|-------|
 | A2UI Hologram Scaling | Resolved (Live WebGL) | DGX SPARK dependency bypassed. Rendering now happens live in the browser via Three.js. |
 | Custom Domain Linking | Handoff | Operator to configure `pmoves.ai` domain in Cloudflare dashboard manually. |
+
 ### PR Readiness
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/docs/AGENTS/AGNOTE4482.md` around lines 1181 - 1186, The "Lane Status"
table contains an unrelated "Custom Domain Linking" row referencing manual
Cloudflare configuration—either remove that row from the Lane Status table or
add a short explanation in the Work Performed / Context sections clarifying why
domain work is part of this audit (refer to the "Lane Status" table and the
"Work Performed" section headings to locate the content); also fix MD058 by
ensuring there is a blank line before and after the markdown table so the linter
stops flagging formatting errors.
pmoves/mk/codex.mk-105-108 (1)

105-108: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid hardcoding 4090 in the recovery command.

The workflow publishes chit-bundle-${target}-${run_id}, and the current default target is spark, so this hint points operators at the wrong artifact name in the common case. Make the example generic or parameterize it with the actual target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/mk/codex.mk` around lines 105 - 108, The example recovery command
hardcodes "4090" which misnames the published artifact; update the hint to use
the actual target variable or a generic placeholder so operators pick the right
artifact name. Replace the literal "chit-bundle-4090-<RUN_ID>" with the
workflow's artifact name pattern (e.g., "chit-bundle-${target}-${RUN_ID}" or use
Makefile's target variable such as "$(target)"/ "$(TARGET)"/ a clear "<TARGET>"
placeholder) and ensure the dirname call still references "$(CHIT_EXPORT_PATH)";
update the echo line near the CHIT_EXPORT_PATH check to reflect this
parameterized artifact name.
.github/workflows/sync-secrets-local.yml-375-380 (1)

375-380: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a real Windows command variant for the 4090 pull instructions.

The text explicitly targets a Windows-native node, but "$APPDATA/pmoves/chit" is Bash syntax. In PowerShell the copy-paste form is $env:APPDATA\pmoves\chit, otherwise the documented recovery path is misleading.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/sync-secrets-local.yml around lines 375 - 380, The Windows
example uses Bash-style "$APPDATA/pmoves/chit" which is incorrect for
PowerShell; update the Windows (4090) echo block (the lines that print the gh
run download command and the subsequent materialize instruction) to show a
proper PowerShell path variant using $env:APPDATA with backslashes (e.g.
$env:APPDATA\pmoves\chit) and appropriate quoting/escaping so Windows users can
copy-paste the command; keep the existing gh run download invocation and the
make -C pmoves secrets-funnel-sync-from-bundle instruction but replace the path
token to the PowerShell form in that echo.
website/hyperdim/saves/breather_surface.json-31-66 (1)

31-66: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Expose the animation parameter this surface actually reads.

The surface uses input.time, but this save only defines scale, colorPhase, hueRange, and saturation. The viewer does not inject an implicit clock field, so phase stays at 0 and this preset loads as static instead of animated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/breather_surface.json` around lines 31 - 66, The
surface function reads input.time (used to compute phase) but the save's
extraParameters array doesn't expose a "time" (or "phase") runtime parameter, so
the preset loads static; add a new extraParameters entry (e.g., name: "time",
min: 0, max: 60, step: 0.01, runtime: 60) to the extraParameters array so the
viewer will drive input.time at runtime and enable the animation referenced by
the phase variable inside function surface.
website/hyperdim/saves/cosmic_bloom.json-90-90 (1)

90-90: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize hue before the HSL-to-RGB branch.

theta can go negative once the animated twist term outweighs u, so hue1/hue also go negative because JS % preserves sign. That feeds a negative hPrime into the conversion code and returns negative RGB channels, which the renderer clamps to black. Use a positive modulo form like ((value % 1) + 1) % 1 before converting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/cosmic_bloom.json` at line 90, The hue calculation can
be negative because theta includes a twist, so in function surface normalize
hue1/hue to [0,1] before converting to RGB: replace the current hue1/hue
expressions (which use % 1) with a positive-modulo normalization ((value % 1) +
1) % 1 for hue1, hue2 and the combined hue so hPrime = h/60 is always
non-negative and the HSL-to-RGB branch never receives negative hPrime; update
references to hue1, hue2, and hue accordingly.
website/hyperdim/saves/dini_surface.json-49-49 (1)

49-49: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the Dini color channels in the valid [0, 1] range.

r_col and g_col can drop to -0.4 here, so large regions will be clipped instead of shaded as intended. Re-center the sine wave into [0, 1] or clamp the channels before returning them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/dini_surface.json` at line 49, The color channels
r_col and g_col in function surface can drop below 0 (e.g., -0.4); update their
calculation to keep values in [0,1] by re-centering the sine output (e.g., use
0.5 + 0.5 * Math.sin(...)) for r_col and g_col, and optionally add a clamp using
Math.max(0, Math.min(1, ...)) before returning to guarantee valid channel
ranges; reference the surface function and the r_col/g_col variables when
applying the change.
website/hyperdim/saves/hypersphere.json-24-29 (1)

24-29: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

uMax does not match the hypersphere parametrization.

The code treats phi1 = input.u and even documents it as [0, PI], but the preset lets u run to . That double-covers the surface and introduces overlapping geometry instead of a single sweep. Either cap uMax at 3.14159 or change the parametrization so the first angle is truly periodic.

Also applies to: 58-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/hypersphere.json` around lines 24 - 29, The preset's
uMax currently equals 6.28318 which double-covers the hypersphere because the
code treats phi1 = input.u as [0, PI]; change uMax to 3.14159 (PI) in the JSON
entries where uMax appears (also update the second occurrence mentioned) so that
phi1=input.u runs 0..PI and the surface isn't overlapped; keep vMax as 3.14159
and leave uSegs/vSegs as-is unless you want different resolution.
website/hyperdim/saves/torus.json-42-42 (1)

42-42: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wrap the torus hue before RGB sector selection.

w4r can push hue above 360, so hp = hue / 60 exceeds 6 and the final else branch colors that slice as if it were always sector 5. Normalize the hue into [0, 360) (or hp into [0, 6)) before the branch.

🎨 Suggested fix
-    const hue = (w4r + 0.5) * 360;
+    const hue = (((w4r + 0.5) * 360) % 360 + 360) % 360;
     const s = 0.8;
     const l = 0.5 + 0.2 * Math.sin(u + v);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/torus.json` at line 42, The hue computed from w4r can
exceed 360 causing hp to be >=6 and hit the wrong RGB branch; inside function
surface normalize the hue (or hp) into the proper range before the sector
selection—e.g., reduce hue into [0,360) (or compute hp = ((hue/60) % 6 + 6) % 6)
so the subsequent if/else ladder using hp selects the correct RGB sector; update
the code around variables hue and hp in function surface to perform this wrap.
website/hyperdim/saves/trefoil_knot.json-88-88 (1)

88-88: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize hue into [0, 1) before computing hp (JS % keeps negatives)

hueRaw = u / 6.283 + hueOffset + irid * Math.sin(v * 2 + u * 3) can be negative with valid parameter ranges (uMin=0, hueOffset∈[0,1], iridescence∈[0,2], sin∈[-1,1]hueRaw down to about -2). In JS, (hueRaw % 1) becomes negative, making hp negative and selecting the wrong HSL sector.

🎨 Suggested fix
-    const hue = (u / 6.283 + hueOffset + irid * Math.sin(v * 2 + u * 3)) % 1.0;
+    const hueRaw = u / 6.283 + hueOffset + irid * Math.sin(v * 2 + u * 3);
+    const hue = ((hueRaw % 1) + 1) % 1;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/trefoil_knot.json` at line 88, The hue calculation can
be negative because JS % preserves sign; inside function surface compute hueRaw
= u / 6.283 + hueOffset + irid * Math.sin(v * 2 + u * 3) then normalize it into
[0,1) before deriving hp (e.g., hue = hueRaw - Math.floor(hueRaw) or use
((hueRaw % 1) + 1) % 1) so hp = hue * 6 is always non-negative; update the hue
usage where hp is computed and ensure subsequent HSL-to-RGB logic uses this
normalized hue.
pmoves/Makefile-1890-1894 (1)

1890-1894: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the configured branch variables during Pages project creation.

These targets define CF_PAGES_BRANCH and PMOVES_AI_BRANCH, but the pages project create commands still hardcode main. If an operator overrides the branch variables, project creation and deploy will disagree about the production branch.

Suggested fix
-    npx wrangler pages project create "$(CF_PAGES_PROJECT)" \
-      --production-branch main 2>&1; \
+    npx wrangler pages project create "$(CF_PAGES_PROJECT)" \
+      --production-branch "$(CF_PAGES_BRANCH)" 2>&1; \
...
-    npx wrangler pages project create "$(PMOVES_AI_PROJECT)" \
-      --production-branch main 2>&1; \
+    npx wrangler pages project create "$(PMOVES_AI_PROJECT)" \
+      --production-branch "$(PMOVES_AI_BRANCH)" 2>&1; \

Also applies to: 1942-1946

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/Makefile` around lines 1890 - 1894, The Makefile invokes `npx wrangler
pages project create "$(CF_PAGES_PROJECT)" --production-branch main` which
hardcodes "main" instead of using the configured variables `CF_PAGES_BRANCH` (or
`PMOVES_AI_BRANCH` where applicable), causing mismatch when branches are
overridden; update the `wrangler pages project create` invocations to use
`$(CF_PAGES_BRANCH)` (or `$(PMOVES_AI_BRANCH)` in the ai-specific targets) for
the `--production-branch` flag so project creation and deploy use the configured
branch variables consistently.
🧹 Nitpick comments (2)
website/hyperdim/saves/mlp.json (1)

39-39: ⚡ Quick win

Cache the generated network instead of rebuilding it per vertex.

seed, hidden_layers, neurons, and activation do not vary across a mesh rebuild, but this function regenerates the same weights for every (u, v) sample. That makes this preset scale with vertex count and becomes the dominant cost once users raise the layer/neuron sliders.

Suggested direction
// Build once per parameter set, then reuse inside surface()
const key = `${nHidden}:${nNeurons}:${actType}:${Math.floor(input.seed)}`;
surface._cache ||= {};
let network = surface._cache[key];
if (!network) {
  network = buildNetwork(...);
  surface._cache = { [key]: network };
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/mlp.json` at line 39, The surface function rebuilds
the entire MLP per vertex; cache the generated network weights keyed by seed,
hidden_layers, neurons and activation so you only build once per parameter set:
create a key from nHidden, nNeurons, actType and Math.floor(input.seed), store
built weights on surface._cache[key], and if absent run a one-time build step
(extract the weight/ bias generation logic currently inside the per-vertex loop
into a buildNetwork phase) then reuse the cached network to compute outputs for
each (u,v); ensure surface clears or updates the cache when any of those
parameters change.
website/styles.css (1)

402-408: ⚡ Quick win

Style the iframe previews here too.

The gallery now renders iframes, but this selector still only targets video, so the preview sizing/background rules are duplicated inline in website/index.html. Moving the iframe presentation into this stylesheet will keep the gallery layout from drifting again.

♻️ Proposed cleanup
-.gallery-item video {
+.gallery-item iframe,
+.gallery-item video {
   width: 100%;
   aspect-ratio: 16 / 9;
-  object-fit: cover;
   display: block;
   background: `#000`;
+  border: 0;
 }
+
+.gallery-item video {
+  object-fit: cover;
+}

Based on learnings: "Replace static gallery video elements with Hyperdimensions viewer iframe embeds (to avoid the 1920x1080 scaling issue)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/styles.css` around lines 402 - 408, The CSS currently styles
.gallery-item video but not the new iframe previews, causing duplicated inline
styles in website/index.html; update the stylesheet by adding a matching rule
for .gallery-item iframe (or combine into a single selector like .gallery-item
video, .gallery-item iframe) with the same properties (width:100%;
aspect-ratio:16/9; object-fit:cover; display:block; background:`#000`) and then
remove the duplicated inline presentation styles from the iframe embeds in
website/index.html so presentation is centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/hardening-validation.yml:
- Around line 10-11: The paths filter for the workflow currently lists the
non-recursive path 'pmoves/integrations/archon' which won't match changes in
nested files; update both occurrences of that path in the workflow's on:
push/pull_request: paths array to a recursive glob like
'pmoves/integrations/archon/**' so subdirectory changes trigger the GHCR matrix
validation (leave the existing '.github/workflows/integrations-ghcr.matrix.json'
entry and the prepublish step 'python3 pmoves/tools/ghcr_local_prepublish.py
--paths-only' unchanged).

In `@pmoves/docs/AGENTS/AGNOTE4482.md`:
- Around line 1167-1180: Update the AGNOTE4482 audit entry to reconcile the
mismatch between the "identical pink dot" context and the PR summary by
explicitly documenting both UI fixes: include the "black canvas fix" details
(ensure the default nautilus fallback evaluates the surface function and force
the Three.js renderer to recalculate geometry when the sidebar collapses in
ui=none mode) and note the playlist link change (replace the TODO with the
requested YouTube playlist link for DARKXSIDE'S School); adjust the Context and
Work Performed sections so they mention both the gallery iframe replacement and
these two core technical fixes so the audit trail accurately reflects all
changes in this PR.
- Around line 1165-1196: Update the shared AGNOTE4482_SIGNOFF_CHECKLIST to
record section-level ACKs for this PR: add entries naming the validated sections
(e.g., "A2UI Hologram Scaling", "Custom Domain Linking"), include the agent
identifier ANTIGRAVITY-GEMINI and the signature
ACK::ANTIGRAVITY-GEMINI::A2UI-HOLOGRAM-SCALING-FIX with the timestamp
2026-05-30, and mark which specific checklist items this agent
reviewed/validated (only the items actually executed), so the checklist reflects
multi-agent merge readiness rather than a single-agent vibe check.

In `@pmoves/Makefile`:
- Around line 665-668: The current shell chain conflates SQL execution failure
and history-insert failure (using _psql < "$$migration" && _psql -c "INSERT INTO
public.pmoves_bootstrap_history(...)" || echo ...) so an insert failure will be
treated as a non-fatal warning; change the flow so you first run _psql <
"$$migration" and if that fails print the warning and continue, but if it
succeeds then run the INSERT into pmoves_bootstrap_history and if that INSERT
fails exit non-zero (stop the target). Update the block around the _psql
invocation and the INSERT (and the analogous block at lines 680-683) to use an
if/then/else or separate conditional commands that only continue on a failed SQL
apply, and abort when the history INSERT (pmoves_bootstrap_history,
filename/migration variables, _psql -c) fails.
- Around line 426-430: The pooler-recreate Makefile target currently always
recreates supabase-pooler; guard it the same way as supa-start by checking
SUPABASE_RUNTIME (or invoking the same guard target used by supa-start) and fail
fast unless SUPABASE_RUNTIME is "compose"; specifically update the
pooler-recreate target to verify SUPABASE_RUNTIME (or call the existing guard
target/function) before running @$(DC) --profile supabase-local up -d
--force-recreate supabase-pooler and print a clear error and non-zero exit when
the runtime is not compose.

In `@pmoves/tools/ghcr_local_prepublish.py`:
- Around line 231-239: Currently the code only checks existence of context_path
and dockerfile_path; change the checks to enforce types: verify
context_path.is_dir() (fail with a ValidationResult and a message like "context
not a directory: {context_rel}") and verify dockerfile_path.is_file() (fail with
a ValidationResult and a message like "dockerfile not a file:
{dockerfile_rel}"), keeping the same return shape (ValidationResult(name=name,
tag=tag, build_ok=False, trivy_ok=None, note=...)) and the same [FAIL]/[PASS]
prints; maintain the final success return (ValidationResult(..., build_ok=True,
note="paths-only")) when both checks pass.

In `@website/hyperdim/index.html`:
- Around line 35-39: The clickable header divs (e.g., elements with IDs
learnMoreHeader, learnMoreCaret and their paired section bodies like
learnMoreBody) must be converted into semantic, keyboard-operable controls:
replace the clickable div with a <button> (or make it role="button" tabbable)
and add aria-expanded and aria-controls attributes pointing to the associated
section body ID so keyboard users can toggle the panel; update the caret state
when toggling and ensure any click handler on the old div is wired to the new
button's click so the same toggle logic runs (apply the same change pattern to
the other headers referenced in the comment).

In `@website/hyperdim/saves/chit_manifold.json`:
- Around line 1-45: The preset uses the old keys surfaceFn, params,
surfaceInput, and animatedParams which the viewer no longer reads; migrate it to
the new save schema by moving the surface function string into surface.code,
convert params into parameters (preserving uMin/uMax/vMin/vMax/uSegs/vSegs),
move surfaceInput values into extraParameters (including
epsilon/curvature/current u/v), and translate animatedParams into display or
extraParameters.animation format expected by the viewer; ensure display includes
camera, shininess, globalSaturation and meta stays intact so the viewer can
restore the scene when _list.json advertises chit_manifold.json.

In `@website/hyperdim/saves/mandelbulb.json`:
- Line 90: getDist can return negative distances which cause the ray step logic
in the main marching loop to push the sample outward; after calling getDist(px,
py, pz) in the ray march loop (where variable d is assigned) clamp d to a
non-negative value (e.g., d = Math.max(d, 0.0)) before performing the hit test
(if (d < epsilon)) and before adjusting r (the r -= d*0.5 and r -= d lines) so
negative DEs won't drive the ray outward; update the code around the main
for-loop that calls getDist to apply this clamp to d.

In `@website/hyperdim/styles.css`:
- Around line 29-43: The collapsed-state CSS doesn't hide the preset/load UI
under .header-panel/.header-panel-row, so update the collapsed selectors
(`#sidebar-wrapper.collapsed` ...) to also target .header-panel and
.header-panel-row (and/or specific header panel descendants that render inputs)
and set display: none so the "button only" state truly removes header controls;
apply this change alongside the existing rules that hide `#controlsBody`,
.control-row, .sidebar-header h2, and `#sidebar-footer` so embeds using
ui=none/preset=... don't leave inputs visible.

In `@website/index.html`:
- Around line 250-280: Add a restrictive sandbox attribute to the gallery
iframes (the <iframe> elements loading src="/hyperdim/...") to prevent those
same-origin viewer pages from reaching the parent origin or storage; if the live
viewer needs to run JS, grant only allow-scripts (do NOT add allow-same-origin
or allow-top-navigation), otherwise use a fully sandboxed iframe with no
allowances or replace with a static preview and communicate via postMessage if
needed.

---

Outside diff comments:
In @.github/workflows/sync-secrets-local.yml:
- Around line 360-367: The summary echo prints empty CGP_FILE, ENV_FILE, and PY
because those shell locals are out of scope in this step; update the workflow
step to recompute or import/persist these variables before the echoes (e.g.,
re-source the script that sets them or read them from prior step outputs/exports
into the environment) so that CGP_FILE, ENV_FILE and PY contain the correct
values when printing the summary and when showing the PMOVES_CGP_FILE decode
command.

In `@website/index.html`:
- Around line 317-328: The two article elements with class "card card-school"
both use the same YouTube playlist href; update the href on the incorrect anchor
so each school card points to its intended playlist. Locate the <article
class="card card-school"> that contains the heading text for the other school
(e.g., "CLOUDFLARE'S School of ...") and change its <a class="btn btn-ghost"
href="..."> href to the correct YouTube playlist URL for that school, keeping
rel="noopener noreferrer" and target="_blank" unchanged; then verify the two
anchors now point to two distinct playlist URLs.

---

Minor comments:
In @.github/workflows/sync-secrets-local.yml:
- Around line 375-380: The Windows example uses Bash-style
"$APPDATA/pmoves/chit" which is incorrect for PowerShell; update the Windows
(4090) echo block (the lines that print the gh run download command and the
subsequent materialize instruction) to show a proper PowerShell path variant
using $env:APPDATA with backslashes (e.g. $env:APPDATA\pmoves\chit) and
appropriate quoting/escaping so Windows users can copy-paste the command; keep
the existing gh run download invocation and the make -C pmoves
secrets-funnel-sync-from-bundle instruction but replace the path token to the
PowerShell form in that echo.

In `@pmoves/docs/AGENTS/AGNOTE4482.md`:
- Around line 1181-1186: The "Lane Status" table contains an unrelated "Custom
Domain Linking" row referencing manual Cloudflare configuration—either remove
that row from the Lane Status table or add a short explanation in the Work
Performed / Context sections clarifying why domain work is part of this audit
(refer to the "Lane Status" table and the "Work Performed" section headings to
locate the content); also fix MD058 by ensuring there is a blank line before and
after the markdown table so the linter stops flagging formatting errors.

In `@pmoves/Makefile`:
- Around line 1890-1894: The Makefile invokes `npx wrangler pages project create
"$(CF_PAGES_PROJECT)" --production-branch main` which hardcodes "main" instead
of using the configured variables `CF_PAGES_BRANCH` (or `PMOVES_AI_BRANCH` where
applicable), causing mismatch when branches are overridden; update the `wrangler
pages project create` invocations to use `$(CF_PAGES_BRANCH)` (or
`$(PMOVES_AI_BRANCH)` in the ai-specific targets) for the `--production-branch`
flag so project creation and deploy use the configured branch variables
consistently.

In `@pmoves/mk/codex.mk`:
- Around line 105-108: The example recovery command hardcodes "4090" which
misnames the published artifact; update the hint to use the actual target
variable or a generic placeholder so operators pick the right artifact name.
Replace the literal "chit-bundle-4090-<RUN_ID>" with the workflow's artifact
name pattern (e.g., "chit-bundle-${target}-${RUN_ID}" or use Makefile's target
variable such as "$(target)"/ "$(TARGET)"/ a clear "<TARGET>" placeholder) and
ensure the dirname call still references "$(CHIT_EXPORT_PATH)"; update the echo
line near the CHIT_EXPORT_PATH check to reflect this parameterized artifact
name.

In `@website/hyperdim/saves/breather_surface.json`:
- Around line 31-66: The surface function reads input.time (used to compute
phase) but the save's extraParameters array doesn't expose a "time" (or "phase")
runtime parameter, so the preset loads static; add a new extraParameters entry
(e.g., name: "time", min: 0, max: 60, step: 0.01, runtime: 60) to the
extraParameters array so the viewer will drive input.time at runtime and enable
the animation referenced by the phase variable inside function surface.

In `@website/hyperdim/saves/cosmic_bloom.json`:
- Line 90: The hue calculation can be negative because theta includes a twist,
so in function surface normalize hue1/hue to [0,1] before converting to RGB:
replace the current hue1/hue expressions (which use % 1) with a positive-modulo
normalization ((value % 1) + 1) % 1 for hue1, hue2 and the combined hue so
hPrime = h/60 is always non-negative and the HSL-to-RGB branch never receives
negative hPrime; update references to hue1, hue2, and hue accordingly.

In `@website/hyperdim/saves/dini_surface.json`:
- Line 49: The color channels r_col and g_col in function surface can drop below
0 (e.g., -0.4); update their calculation to keep values in [0,1] by re-centering
the sine output (e.g., use 0.5 + 0.5 * Math.sin(...)) for r_col and g_col, and
optionally add a clamp using Math.max(0, Math.min(1, ...)) before returning to
guarantee valid channel ranges; reference the surface function and the
r_col/g_col variables when applying the change.

In `@website/hyperdim/saves/hypersphere.json`:
- Around line 24-29: The preset's uMax currently equals 6.28318 which
double-covers the hypersphere because the code treats phi1 = input.u as [0, PI];
change uMax to 3.14159 (PI) in the JSON entries where uMax appears (also update
the second occurrence mentioned) so that phi1=input.u runs 0..PI and the surface
isn't overlapped; keep vMax as 3.14159 and leave uSegs/vSegs as-is unless you
want different resolution.

In `@website/hyperdim/saves/torus.json`:
- Line 42: The hue computed from w4r can exceed 360 causing hp to be >=6 and hit
the wrong RGB branch; inside function surface normalize the hue (or hp) into the
proper range before the sector selection—e.g., reduce hue into [0,360) (or
compute hp = ((hue/60) % 6 + 6) % 6) so the subsequent if/else ladder using hp
selects the correct RGB sector; update the code around variables hue and hp in
function surface to perform this wrap.

In `@website/hyperdim/saves/trefoil_knot.json`:
- Line 88: The hue calculation can be negative because JS % preserves sign;
inside function surface compute hueRaw = u / 6.283 + hueOffset + irid *
Math.sin(v * 2 + u * 3) then normalize it into [0,1) before deriving hp (e.g.,
hue = hueRaw - Math.floor(hueRaw) or use ((hueRaw % 1) + 1) % 1) so hp = hue * 6
is always non-negative; update the hue usage where hp is computed and ensure
subsequent HSL-to-RGB logic uses this normalized hue.

---

Nitpick comments:
In `@website/hyperdim/saves/mlp.json`:
- Line 39: The surface function rebuilds the entire MLP per vertex; cache the
generated network weights keyed by seed, hidden_layers, neurons and activation
so you only build once per parameter set: create a key from nHidden, nNeurons,
actType and Math.floor(input.seed), store built weights on surface._cache[key],
and if absent run a one-time build step (extract the weight/ bias generation
logic currently inside the per-vertex loop into a buildNetwork phase) then reuse
the cached network to compute outputs for each (u,v); ensure surface clears or
updates the cache when any of those parameters change.

In `@website/styles.css`:
- Around line 402-408: The CSS currently styles .gallery-item video but not the
new iframe previews, causing duplicated inline styles in website/index.html;
update the stylesheet by adding a matching rule for .gallery-item iframe (or
combine into a single selector like .gallery-item video, .gallery-item iframe)
with the same properties (width:100%; aspect-ratio:16/9; object-fit:cover;
display:block; background:`#000`) and then remove the duplicated inline
presentation styles from the iframe embeds in website/index.html so presentation
is centralized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ae42a471-e152-41e6-a4d6-bc01d93dd50e

📥 Commits

Reviewing files that changed from the base of the PR and between 82324d9 and d34ae94.

⛔ Files ignored due to path filters (4)
  • website/assets/gallery/chit_hero_and_mindmap.png is excluded by !**/*.png
  • website/assets/gallery/group_c1_hologram.mp4 is excluded by !**/*.mp4
  • website/assets/gallery/group_c3_hologram.mp4 is excluded by !**/*.mp4
  • website/assets/gallery/group_c5_hologram.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (44)
  • .github/workflows/hardening-validation.yml
  • .github/workflows/sync-secrets-local.yml
  • pmoves/Makefile
  • pmoves/docs/AGENTS/AGNOTE4482.md
  • pmoves/mk/codex.mk
  • pmoves/scripts/supabase/find_db_container.sh
  • pmoves/tools/ghcr_local_prepublish.py
  • website/assets/gallery/chit_mindmap.webp
  • website/hyperdim/index.html
  • website/hyperdim/saves/_list.json
  • website/hyperdim/saves/beats_c1.json
  • website/hyperdim/saves/beats_c3.json
  • website/hyperdim/saves/beats_c5.json
  • website/hyperdim/saves/beats_constellation.json
  • website/hyperdim/saves/breather_surface.json
  • website/hyperdim/saves/chit_manifold.json
  • website/hyperdim/saves/cosmic_bloom.json
  • website/hyperdim/saves/dini_surface.json
  • website/hyperdim/saves/donut.json
  • website/hyperdim/saves/fourier.json
  • website/hyperdim/saves/hopf_fibration.json
  • website/hyperdim/saves/hotdog.json
  • website/hyperdim/saves/hypersphere.json
  • website/hyperdim/saves/jellyfish.json
  • website/hyperdim/saves/klein_bottle.json
  • website/hyperdim/saves/mandelbulb.json
  • website/hyperdim/saves/mlp.json
  • website/hyperdim/saves/mobius_strip.json
  • website/hyperdim/saves/nautilus.json
  • website/hyperdim/saves/ocean_planet.json
  • website/hyperdim/saves/perceptron.json
  • website/hyperdim/saves/planet.json
  • website/hyperdim/saves/rose.json
  • website/hyperdim/saves/shells.json
  • website/hyperdim/saves/sphere_tutorial.json
  • website/hyperdim/saves/taylor.json
  • website/hyperdim/saves/torus.json
  • website/hyperdim/saves/torus_knot.json
  • website/hyperdim/saves/trefoil_knot.json
  • website/hyperdim/saves/weierstrass.json
  • website/hyperdim/styles.css
  • website/index.html
  • website/styles.css
  • website/wrangler.toml

Comment on lines +10 to +11
- '.github/workflows/integrations-ghcr.matrix.json'
- 'pmoves/integrations/archon'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/hardening-validation.yml"

echo "=== File existence / size ==="
ls -l "$FILE" || true
echo

echo "=== Lines 1-80 ==="
nl -ba "$FILE" | sed -n '1,120p'
echo

echo "=== Search for specific path entries ==="
rg -n "integrations-ghcr\.matrix\.json|pmoves/integrations/archon" "$FILE" || true
echo

echo "=== Search for ghcr_local_prepublish step ==="
rg -n "ghcr_local_prepublish\.py|--paths-only" "$FILE" || true
echo

echo "=== Lines around ghcr_local_prepublish invocation (if found) ==="
# If we found a match, print a window around the first match line
LINE=$(rg -n "ghcr_local_prepublish\.py" "$FILE" | head -n1 | cut -d: -f1 || true)
if [[ -n "${LINE:-}" ]]; then
  START=$((LINE-40))
  END=$((LINE+60))
  nl -ba "$FILE" | sed -n "${START},${END}p"
else
  echo "No ghcr_local_prepublish.py invocation found."
fi

echo "=== Search for StepSecurity presence ==="
rg -n "StepSecurity|stepsecurity|security" "$FILE" || true

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 272


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/hardening-validation.yml"

echo "=== File existence / size ==="
ls -l "$FILE" || true
echo

echo "=== Lines 1-140 ==="
nl -ba "$FILE" | sed -n '1,160p'
echo

echo "=== Search for specific path entries ==="
rg -n "integrations-ghcr\.matrix\.json|pmoves/integrations/archon" "$FILE" || true
echo

echo "=== Search for ghcr_local_prepublish step ==="
rg -n "ghcr_local_prepublish\.py|--paths-only" "$FILE" || true
echo

echo "=== Lines around ghcr_local_prepublish invocation ==="
LINE=$(rg -n "ghcr_local_prepublish\.py" "$FILE" | head -n1 | cut -d: -f1 || true)
if [[ -n "${LINE:-}" ]]; then
  START=$((LINE-60))
  END=$((LINE+80))
  nl -ba "$FILE" | sed -n "${START},${END}p"
fi

echo "=== Search for StepSecurity presence ==="
rg -n "StepSecurity|stepsecurity|step-security|Step Security" "$FILE" || true

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/hardening-validation.yml"

echo "=== Lines 1-140 (with line numbers via awk) ==="
awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,140p'

echo
echo "=== Search for specific path entries ==="
rg -n "integrations-ghcr\.matrix\.json|pmoves/integrations/archon" "$FILE" || true

echo
echo "=== Search for ghcr_local_prepublish step ==="
rg -n "ghcr_local_prepublish\.py|--paths-only" "$FILE" || true

echo
echo "=== Show StepSecurity/secret-hardening markers ==="
rg -n "StepSecurity|stepsecurity|step-security|secrets|token|registry" "$FILE" || true

echo
echo "=== Show context around first match for ghcr_local_prepublish.py ==="
MATCH_LINE=$(rg -n "ghcr_local_prepublish\.py" "$FILE" | head -n1 | cut -d: -f1 || true)
if [[ -n "${MATCH_LINE:-}" ]]; then
  START=$((MATCH_LINE-60))
  END=$((MATCH_LINE+60))
  awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n "${START},${END}p"
fi

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 12580


🌐 Web query:

GitHub Actions workflow trigger paths filter match directory without wildcard paths: - some/dirvssome/dir/**``

💡 Result:

In GitHub Actions, the behavior of path filters depends on the glob pattern provided, as the system follows standard glob matching rules [1][2]. Using paths: - some/dir When you specify a directory path without a wildcard, GitHub Actions interprets this as a pattern that matches the directory itself and any files located exactly within that directory [2]. However, it does not automatically include files in nested subdirectories [2]. For example, some/dir will trigger on some/dir/file.txt but will not trigger on some/dir/subdir/file.txt. Using paths: - some/dir/ The `` wildcard is the recursive glob operator [13]. By appending /, you explicitly instruct the filter to match all files within the specified directory and all of its nested subdirectories, regardless of depth [2]. This is the standard way to ensure that any change within a directory tree triggers the workflow. Summary of Differences: - `some/dir`: Matches files directly inside `some/dir` only. - `some/dir/`: Matches files inside `some/dir` and all files in any subdirectories recursively. If your goal is to trigger the workflow whenever any file changes within a directory tree, `some/dir/` is the correct and necessary syntax [2]. Both patterns are treated as glob expressions evaluated relative to the root of the repository [2].

Citations:


Broaden Archon path filter so nested changes trigger the GHCR matrix validation

The workflow on: push/pull_request: paths entries add only pmoves/integrations/archon (lines 11 and 19). That non-recursive glob won’t trigger on changes in subdirectories under pmoves/integrations/archon/, so the new GHCR path gate can be skipped for the Archon changes it’s meant to validate. The added python3 pmoves/tools/ghcr_local_prepublish.py --paths-only step is otherwise wired correctly, and StepSecurity hardening remains present.

Suggested fix
-      - 'pmoves/integrations/archon'
+      - 'pmoves/integrations/archon/**'
...
-      - 'pmoves/integrations/archon'
+      - 'pmoves/integrations/archon/**'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- '.github/workflows/integrations-ghcr.matrix.json'
- 'pmoves/integrations/archon'
- '.github/workflows/integrations-ghcr.matrix.json'
- 'pmoves/integrations/archon/**'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/hardening-validation.yml around lines 10 - 11, The paths
filter for the workflow currently lists the non-recursive path
'pmoves/integrations/archon' which won't match changes in nested files; update
both occurrences of that path in the workflow's on: push/pull_request: paths
array to a recursive glob like 'pmoves/integrations/archon/**' so subdirectory
changes trigger the GHCR matrix validation (leave the existing
'.github/workflows/integrations-ghcr.matrix.json' entry and the prepublish step
'python3 pmoves/tools/ghcr_local_prepublish.py --paths-only' unchanged).

Comment on lines +1165 to +1196
## 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 `<video>` elements in `website/index.html` gallery with `<iframe>` embeds targeting the Hyperdimensions viewer, successfully resolving the 1920x1080 scaling issue via live rendering.

### Lane Status
| Lane | Status | Notes |
|------|--------|-------|
| A2UI Hologram Scaling | Resolved (Live WebGL) | DGX SPARK dependency bypassed. Rendering now happens live in the browser via Three.js. |
| Custom Domain Linking | Handoff | Operator to configure `pmoves.ai` domain in Cloudflare dashboard manually. |

### PR Readiness
The branch is ready to commit the UI changes. Proposed logical commit:
`feat(website): replace A2UI mp4s with live Hyperdimensions WebGL embeds to fix 1920x1080 scaling`

### Agent ACK
- Agent: `ANTIGRAVITY-GEMINI`
- Signature: `ACK::ANTIGRAVITY-GEMINI::A2UI-HOLOGRAM-SCALING-FIX`
- Timestamp: `2026-05-30`

<!-- GRAPHITI_MARK: ANTIGRAVITY-GEMINI::A2UI-HOLOGRAM-SCALING-FIX::2026-05-30 -->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing AGNOTE4482 signoff checklist update.

The AGNOTE4482 lane protocol requires updating the shared signoff gate (AGNOTE4482_SIGNOFF_CHECKLIST.md) with section-level ACKs. This audit entry documents completed work but doesn't reference which signoff checklist sections were validated or marked complete.

Per the protocol defined at lines 19-23: "Each participating agent signs only for the sections they actually reviewed or executed. Merge readiness is a multi-agent decision, not a single-agent vibe check."

Suggested addition

Add after line 1189:

 The branch is ready to commit the UI changes. Proposed logical commit:
 `feat(website): replace A2UI mp4s with live Hyperdimensions WebGL embeds to fix 1920x1080 scaling`
 
+### Signoff Checklist Status
+Updated `AGNOTE4482_SIGNOFF_CHECKLIST.md`:
+- ✅ §[X.X] [Description of relevant checklist section]
+- ✅ §[Y.Y] [Description of relevant checklist section]
+
 ### Agent ACK

Based on coding guidelines: agents must update the shared signoff gate (AGNOTE4482_SIGNOFF_CHECKLIST) with section-level ACKs.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 1182-1182: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/docs/AGENTS/AGNOTE4482.md` around lines 1165 - 1196, Update the shared
AGNOTE4482_SIGNOFF_CHECKLIST to record section-level ACKs for this PR: add
entries naming the validated sections (e.g., "A2UI Hologram Scaling", "Custom
Domain Linking"), include the agent identifier ANTIGRAVITY-GEMINI and the
signature ACK::ANTIGRAVITY-GEMINI::A2UI-HOLOGRAM-SCALING-FIX with the timestamp
2026-05-30, and mark which specific checklist items this agent
reviewed/validated (only the items actually executed), so the checklist reflects
multi-agent merge readiness rather than a single-agent vibe check.

Comment on lines +1167 to +1180
### 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 `<video>` elements in `website/index.html` gallery with `<iframe>` embeds targeting the Hyperdimensions viewer, successfully resolving the 1920x1080 scaling issue via live rendering.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inconsistency between audit entry and PR scope.

The Context section describes fixing an "identical pink dot" gallery issue (line 1168), but the PR title and summary state this PR "resolves black WebGL canvas issues in the default and embed viewers." The Work Performed section lists gallery iframe replacements but omits the core technical fixes mentioned in the PR summary:

  1. Black canvas fix: "ensuring the default nautilus fallback evaluates the surface function and forcing the Three.js renderer to recalculate geometry when the sidebar collapses in ui=none mode"
  2. Playlist link update: PR summary mentions "replaces a TODO with the requested YouTube playlist link for DARKXSIDE'S School"

The audit trail should accurately reflect all work performed in this PR. Either reconcile the "pink dot" vs "black canvas" discrepancy or document both issues if they're related.

Suggested additions to Work Performed

Add after line 1173:

 ### Work Performed
+- Fixed black WebGL canvas rendering issues in Hyperdimensions viewer:
+  - Ensured default nautilus preset evaluates the surface function properly
+  - Added geometry recalculation trigger when sidebar collapses in `ui=none` embed mode
 - Bypassed the stubbed Remotion 2D generator and implemented a live interactive 3D WebGL solution on the landing page.

Add after line 1179:

   - `beats_c1.json`: Cluster 1 (High fitness, tight curvature, very bright)
 - Replaced the three static `<video>` elements in `website/index.html` gallery with `<iframe>` embeds targeting the Hyperdimensions viewer, successfully resolving the 1920x1080 scaling issue via live rendering.
+- Updated Cloudflare School playlist button URL to link DARKXSIDE'S School playlist.

Based on coding guidelines: AGNOTE4482 protocol requires documenting UI fixes as trail entries. As per coding guidelines, the documented fix direction includes recording additional UI fixes applied.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/docs/AGENTS/AGNOTE4482.md` around lines 1167 - 1180, Update the
AGNOTE4482 audit entry to reconcile the mismatch between the "identical pink
dot" context and the PR summary by explicitly documenting both UI fixes: include
the "black canvas fix" details (ensure the default nautilus fallback evaluates
the surface function and force the Three.js renderer to recalculate geometry
when the sidebar collapses in ui=none mode) and note the playlist link change
(replace the TODO with the requested YouTube playlist link for DARKXSIDE'S
School); adjust the Context and Work Performed sections so they mention both the
gallery iframe replacement and these two core technical fixes so the audit trail
accurately reflects all changes in this PR.

Comment thread pmoves/Makefile
Comment on lines +426 to +430
.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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard pooler-recreate against CLI runtime.

Lines 427-429 always start supabase-pooler through compose. If SUPABASE_RUNTIME=cli, this bypasses the existing runtime-guard flow and creates mixed Supabase state on the same machine. This target should fail fast unless the runtime is compose, or call the same guard used by supa-start.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/Makefile` around lines 426 - 430, The pooler-recreate Makefile target
currently always recreates supabase-pooler; guard it the same way as supa-start
by checking SUPABASE_RUNTIME (or invoking the same guard target used by
supa-start) and fail fast unless SUPABASE_RUNTIME is "compose"; specifically
update the pooler-recreate target to verify SUPABASE_RUNTIME (or call the
existing guard target/function) before running @$(DC) --profile supabase-local
up -d --force-recreate supabase-pooler and print a clear error and non-zero exit
when the runtime is not compose.

Comment thread pmoves/Makefile
Comment on lines +665 to +668
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"; \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t treat “applied but not recorded” as a warning.

The apply && insert-history || warn chain collapses two different failures into one path. If the SQL applies successfully but the pmoves_bootstrap_history insert fails, the target continues and the next run will replay that already-applied file against a mutated database.

Split the flow so only SQL execution failures are skippable; once a file has been applied, failure to record it should stop the target.

Also applies to: 680-683

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/Makefile` around lines 665 - 668, The current shell chain conflates
SQL execution failure and history-insert failure (using _psql < "$$migration" &&
_psql -c "INSERT INTO public.pmoves_bootstrap_history(...)" || echo ...) so an
insert failure will be treated as a non-fatal warning; change the flow so you
first run _psql < "$$migration" and if that fails print the warning and
continue, but if it succeeds then run the INSERT into pmoves_bootstrap_history
and if that INSERT fails exit non-zero (stop the target). Update the block
around the _psql invocation and the INSERT (and the analogous block at lines
680-683) to use an if/then/else or separate conditional commands that only
continue on a failed SQL apply, and abort when the history INSERT
(pmoves_bootstrap_history, filename/migration variables, _psql -c) fails.

Comment on lines +35 to +39
<div class="section-header" id="learnMoreHeader">
<span class="section-caret" id="learnMoreCaret">▶</span>
<span class="section-title">Learn More</span>
</div>
<div class="section-body" id="learnMoreBody">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use semantic buttons for the collapsible section headers.

These headers are clickable divs, so they are not focusable or keyboard-operable. That blocks keyboard-only users from opening collapsed panels like Display, Surface function, and Outputs.

Also applies to: 59-63, 142-145, 200-203, 217-220

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/index.html` around lines 35 - 39, The clickable header divs
(e.g., elements with IDs learnMoreHeader, learnMoreCaret and their paired
section bodies like learnMoreBody) must be converted into semantic,
keyboard-operable controls: replace the clickable div with a <button> (or make
it role="button" tabbable) and add aria-expanded and aria-controls attributes
pointing to the associated section body ID so keyboard users can toggle the
panel; update the caret state when toggling and ensure any click handler on the
old div is wired to the new button's click so the same toggle logic runs (apply
the same change pattern to the other headers referenced in the comment).

Comment on lines +1 to +45
{
"surfaceFn": "function surface(input) {\n const u = (input.u - 0.5) * 10;\n const v = (input.v - 0.5) * 10;\n const eps = input.epsilon || 0.0;\n \n const z = Math.sin(u)*Math.cos(v) * eps;\n \n return {\n x: u, \n y: v, \n z: z, \n r: 0.5, \n g: 0.5, \n b: 0.5, \n a: 0.8\n };\n}",
"params": {
"uMin": 0,
"uMax": 1,
"vMin": 0,
"vMax": 1,
"uSegs": 150,
"vSegs": 150
},
"surfaceInput": {
"u": 0,
"v": 0,
"curvature": 0.0,
"epsilon": 0.44662856918050364
},
"animatedParams": [
{
"name": "epsilon",
"playing": true,
"min": 0.24662856918050363,
"max": 0.6466285691805036,
"step": 0.01,
"time": 5,
"phase": 0
}
],
"camera": {
"position": {
"x": 5,
"y": 5,
"z": 2
},
"target": {
"x": 0,
"y": 0,
"z": 0
}
},
"shininess": 150,
"globalSaturation": 1.5,
"meta": {
"inferred_shape": "Flat",
"delta_proxy": 0.44662856918050364
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Migrate this preset to the current save schema before shipping it in the load menu.

The viewer now loads surface.code, parameters, extraParameters, and display, but this file still uses the old surfaceFn / params / surfaceInput / animatedParams layout. Because _list.json still advertises chit_manifold.json, choosing it will not restore the saved manifold scene.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/chit_manifold.json` around lines 1 - 45, The preset
uses the old keys surfaceFn, params, surfaceInput, and animatedParams which the
viewer no longer reads; migrate it to the new save schema by moving the surface
function string into surface.code, convert params into parameters (preserving
uMin/uMax/vMin/vMax/uSegs/vSegs), move surfaceInput values into extraParameters
(including epsilon/curvature/current u/v), and translate animatedParams into
display or extraParameters.animation format expected by the viewer; ensure
display includes camera, shininess, globalSaturation and meta stays intact so
the viewer can restore the scene when _list.json advertises chit_manifold.json.

}
],
"surface": {
"code": "function surface(input) {\n const u = input.u; // Theta\n const v = input.v; // Phi\n \n // Optimized access to params\n const power = input.Power;\n const maxIter = input.Iterations;\n const bailout = input.Bailout;\n const bailoutSq = bailout * bailout;\n const detail = input.Detail;\n \n // Dynamic epsilon based on detail\n const epsilon = 1.0 / (detail * 10);\n const maxRaySteps = 100;\n \n const cx = input.Cx;\n const cy = input.Cy;\n const cz = input.Cz;\n \n const sinU = Math.sin(u);\n const dx = sinU * Math.cos(v);\n const dy = sinU * Math.sin(v);\n const dz = Math.cos(u);\n \n let r = 2.0;\n let hit = false;\n \n // Inlined GetDist for performance (avoids function call overhead in tight loop)\n // Improved Derivative Logic to reduce spikes\n function getDist(x, y, z) {\n let zx = x, zy = y, zz = z;\n let dr = 1.0;\n let r2 = zx*zx + zy*zy + zz*zz;\n \n for (let i = 0; i < maxIter; i++) {\n if (r2 > bailoutSq) break;\n \n // Optimize: use raw math, minimize object creation\n let rad = Math.sqrt(r2);\n // Safe acos for theta (clamp to -1..1)\n let theta = Math.acos(Math.max(-1, Math.min(1, zz / rad)));\n let phi = Math.atan2(zy, zx);\n \n let zr = Math.pow(rad, power);\n let thetaP = theta * power;\n let phiP = phi * power;\n \n // Julia Derivative: dr = power * r^(power-1) * dr\n // We can optimize pow calls\n dr = Math.pow(rad, power - 1) * power * dr + 1.0;\n \n const sinThetaP = Math.sin(thetaP);\n zx = zr * sinThetaP * Math.cos(phiP);\n zy = zr * sinThetaP * Math.sin(phiP);\n zz = zr * Math.cos(thetaP);\n \n zx += cx;\n zy += cy;\n zz += cz;\n \n r2 = zx*zx + zy*zy + zz*zz;\n }\n \n const rad = Math.sqrt(r2);\n // Soften the DE to reduce sharp spikes (over-stepping)\n // Multiplying by 0.5 * log(r) * r / dr is standard, but we cap dr to avoid infinite spikes\n if (dr < 1.0) dr = 1.0;\n return 0.5 * Math.log(rad) * rad / dr;\n }\n\n for(let i=0; i<maxRaySteps; i++) {\n const px = dx * r;\n const py = dy * r;\n const pz = dz * r;\n \n let d = getDist(px, py, pz);\n \n // Spike Fix: Clamp maximum step size to prevent overshooting into the shape\n // especially when near complex details\n if (d > 0.2) d = 0.2;\n\n if (d < epsilon) {\n hit = true;\n r -= d * 0.5;\n break;\n }\n \n r -= d;\n if (r < 0) { r = 0; break; }\n }\n \n const finalX = dx * r;\n const finalY = dy * r;\n const finalZ = dz * r;\n \n // Coloring (Compute Trap)\n let trap = 1e20;\n let iters = 0;\n \n if (hit) {\n let zx = finalX, zy = finalY, zz = finalZ;\n let r2 = 0;\n for (let i = 0; i < maxIter; i++) {\n r2 = zx*zx + zy*zy + zz*zz;\n if (r2 > bailoutSq) break;\n \n trap = Math.min(trap, r2);\n \n let rad = Math.sqrt(r2);\n let theta = Math.acos(Math.max(-1, Math.min(1, zz / rad)));\n let phi = Math.atan2(zy, zx);\n \n let zr = Math.pow(rad, power);\n let thetaP = theta * power;\n let phiP = phi * power;\n \n const sinThetaP = Math.sin(thetaP);\n zx = zr * sinThetaP * Math.cos(phiP);\n zy = zr * sinThetaP * Math.sin(phiP);\n zz = zr * Math.cos(thetaP);\n \n zx += cx;\n zy += cy;\n zz += cz;\n iters = i;\n }\n }\n\n let red, green, blue;\n if (hit) {\n const t = Math.sqrt(trap);\n const col = t * 0.8 + (iters / maxIter) * 0.2;\n red = 0.5 + 0.5 * Math.cos(col * 4 + 0.5);\n green = 0.5 + 0.5 * Math.cos(col * 4 + 1.5);\n blue = 0.5 + 0.5 * Math.cos(col * 4 + 2.5);\n } else {\n red = 0; green = 0; blue = 0;\n }\n \n const alpha = hit ? 1.0 : 0.0;\n \n return { x: finalX, y: finalY, z: finalZ, r: red, g: green, b: blue, a: alpha };\n}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clamp signed distances before the hit test.

getDist() can return a negative value after the ray steps inside the fractal. The current if (d < epsilon) path treats that as a hit and then does r -= d * 0.5, which actually pushes the sample back outward. That makes the returned point depend on overshoot rather than the surface and will show up as unstable shells/artifacts near the bulb boundary.

Suggested fix
-        let d = getDist(px, py, pz);
+        let d = getDist(px, py, pz);
+        if (!Number.isFinite(d)) break;

-        if (d > 0.2) d = 0.2;
+        if (d < 0) d = 0;
+        if (d > 0.2) d = 0.2;

-        if (d < epsilon) {
+        if (d <= epsilon) {
             hit = true;
-            r -= d * 0.5;
             break;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/saves/mandelbulb.json` at line 90, getDist can return
negative distances which cause the ray step logic in the main marching loop to
push the sample outward; after calling getDist(px, py, pz) in the ray march loop
(where variable d is assigned) clamp d to a non-negative value (e.g., d =
Math.max(d, 0.0)) before performing the hit test (if (d < epsilon)) and before
adjusting r (the r -= d*0.5 and r -= d lines) so negative DEs won't drive the
ray outward; update the code around the main for-loop that calls getDist to
apply this clamp to d.

Comment on lines +29 to +43
/* Collapsed state: only show the button, no panel visuals */
#sidebar-wrapper.collapsed #sidebar {
width: auto;
height: auto;
padding: 0;
background: transparent;
border: none;
overflow: visible;
}

#sidebar-wrapper.collapsed #controlsBody,
#sidebar-wrapper.collapsed .sidebar-header h2,
#sidebar-wrapper.collapsed .control-row,
#sidebar-wrapper.collapsed #sidebar-footer {
display: none;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Collapsed mode still leaves header controls visible.

This rule hides #controlsBody, h2, .control-row, and #sidebar-footer, but the preset/load UI lives under .header-panel / .header-panel-row, so the embed can still render inputs over the canvas in ui=none. Hide the header panel too, otherwise the “button only” collapsed state is never fully reached.

💡 Proposed fix
 `#sidebar-wrapper.collapsed` `#controlsBody`,
 `#sidebar-wrapper.collapsed` .sidebar-header h2,
+#sidebar-wrapper.collapsed .header-panel,
+#sidebar-wrapper.collapsed .header-panel-row,
 `#sidebar-wrapper.collapsed` .control-row,
 `#sidebar-wrapper.collapsed` `#sidebar-footer` {
     display: none;
 }

Based on learnings: "Ensure URL parameter support for embedding/headless mode: preset=... and ui=none should be parsed and applied."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/* Collapsed state: only show the button, no panel visuals */
#sidebar-wrapper.collapsed #sidebar {
width: auto;
height: auto;
padding: 0;
background: transparent;
border: none;
overflow: visible;
}
#sidebar-wrapper.collapsed #controlsBody,
#sidebar-wrapper.collapsed .sidebar-header h2,
#sidebar-wrapper.collapsed .control-row,
#sidebar-wrapper.collapsed #sidebar-footer {
display: none;
/* Collapsed state: only show the button, no panel visuals */
`#sidebar-wrapper.collapsed` `#sidebar` {
width: auto;
height: auto;
padding: 0;
background: transparent;
border: none;
overflow: visible;
}
`#sidebar-wrapper.collapsed` `#controlsBody`,
`#sidebar-wrapper.collapsed` .sidebar-header h2,
`#sidebar-wrapper.collapsed` .header-panel,
`#sidebar-wrapper.collapsed` .header-panel-row,
`#sidebar-wrapper.collapsed` .control-row,
`#sidebar-wrapper.collapsed` `#sidebar-footer` {
display: none;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/hyperdim/styles.css` around lines 29 - 43, The collapsed-state CSS
doesn't hide the preset/load UI under .header-panel/.header-panel-row, so update
the collapsed selectors (`#sidebar-wrapper.collapsed` ...) to also target
.header-panel and .header-panel-row (and/or specific header panel descendants
that render inputs) and set display: none so the "button only" state truly
removes header controls; apply this change alongside the existing rules that
hide `#controlsBody`, .control-row, .sidebar-header h2, and `#sidebar-footer` so
embeds using ui=none/preset=... don't leave inputs visible.

Comment thread website/index.html
Comment on lines +250 to +280
<iframe
src="/hyperdim/?preset=beats_c5.json&ui=none"
title="Hologram Cluster 5 — Allegro Balanced Bright"
style="width: 100%; height: 100%; min-height: 300px; border: none; aspect-ratio: 16/9; pointer-events: none; border-radius: 8px;"
loading="lazy">
</iframe>
<figcaption>
<strong>808 SIGNS / RAKS SLOW</strong>
<span>Cluster 5 &middot; Allegro Balanced Bright</span>
</figcaption>
</figure>

<figure class="gallery-item">
<iframe
src="/hyperdim/?preset=beats_c3.json&ui=none"
title="Hologram Cluster 3 — Allegro Balanced Deep"
style="width: 100%; height: 100%; min-height: 300px; border: none; aspect-ratio: 16/9; pointer-events: none; border-radius: 8px;"
loading="lazy">
</iframe>
<figcaption>
<strong>THE ALIEN IN THE WINDOW</strong>
<span>Cluster 3 &middot; Allegro Balanced Deep</span>
</figcaption>
</figure>

<figure class="gallery-item">
<iframe
src="/hyperdim/?preset=beats_c1.json&ui=none"
title="Hologram Cluster 1"
style="width: 100%; height: 100%; min-height: 300px; border: none; aspect-ratio: 16/9; pointer-events: none; border-radius: 8px;"
loading="lazy">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sandbox the Hyperdimensions preview iframes.

These gallery previews load a same-origin page that executes substantial viewer logic. Without a sandbox, any script execution bug inside /hyperdim/ can reach the landing-page DOM and origin storage through window.top. Lock the previews down to the minimum capabilities they actually need, or switch the gallery to a static preview if the live viewer requires broader privileges.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/index.html` around lines 250 - 280, Add a restrictive sandbox
attribute to the gallery iframes (the <iframe> elements loading
src="/hyperdim/...") to prevent those same-origin viewer pages from reaching the
parent origin or storage; if the live viewer needs to run JS, grant only
allow-scripts (do NOT add allow-same-origin or allow-top-navigation), otherwise
use a fully sandboxed iframe with no allowances or replace with a static preview
and communicate via postMessage if needed.

@POWERFULMOVES

Copy link
Copy Markdown
Owner Author

Closing as superseded. Audited commit-by-commit against main (this branch is 39 commits behind):

Branch retained (not deleted) for reference per repo convention. Nothing else here needs to land.

POWERFULMOVES added a commit that referenced this pull request Jun 2, 2026
…DO (#1682)

The live site showed a "TODO(founder): replace with DARKXSIDE'S School playlist
URL" string as the Cloudflare School card's button text with href="#". Point it
at the existing DARKXSIDE playlist (same list used by the DARKXSIDE School card)
so visitors get a working link instead of a placeholder.

Salvaged from the superseded #1677 (fix/ghcr-matrix-paths-gate); all of that
branch's other changes — website gallery/viewer, deploy targets, GHCR matrix
paths gate + tool, sync-secrets shell:bash, supabase pooler fix — are already on
main via #1655/#1672/#1675/#1678 and prior CI merges. This playlist link was the
only piece not yet on main.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@POWERFULMOVES
POWERFULMOVES deleted the fix/ghcr-matrix-paths-gate branch June 13, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant