Files
upbeatBytes/tools/glb-split/audit-head.mjs
T
thejayman77 89c0fbe1f6 Sync repo to deployed state: SEO recovery, Publishing Desk, Play games, emoji picker
The deploy pipeline runs from the working tree, so a wave of shipped features
had never been committed. This snapshots git to what's actually running.

SEO impression recovery (live + verified):
- Duplicate /a/{id} now 301-redirect to their canonical twin instead of 404
  (a hard 404 silently dropped already-indexed URLs and tanked impressions).
- Dedup representative selection reworked: accepted/serveable -> established
  rep (URL stability) -> quality score, so an accepted page never retires to a
  rejected rep and an indexed canonical doesn't churn when a newer twin arrives.
- HEAD /a/{id} returns the same status as GET (api_route GET+HEAD) instead of
  falling through to the static mount and 404ing.
- `dedup --force-recluster`: cycle-locked, model-free re-cluster to re-apply the
  policy to the existing corpus (shared cycle_lock context manager).
- CLI honors GOODNEWS_DB for its default --db (was silently ignored).

Publishing Desk (admin tool to post highlights to X via Web Intents):
- publishing.py queue/rank/handle-resolution; admin UI; full searchable emoji
  picker (bundled data, no CDN) for the blurb editor.

Play games + site:
- Bloom (word-wheel), Memory Match, daily ritual set, Zen Den (dev-gated).
- English-only language gate; source prospecting; paywall + dedup hardening.

Tests: full suite green (349). Ignores tightened (node_modules, data/*.db).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:32:27 -04:00

48 lines
2.5 KiB
JavaScript

// Drill into the head/mouth region. Front = +Z. Looks at body vertices in the
// front quarter and reports the vertical (Y) profile from nose back, plus the
// lowest points near the mouth — to test "lower lip sits below the body".
import { NodeIO } from '@gltf-transform/core';
import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
const io = new NodeIO().registerExtensions(ALL_EXTENSIONS);
const doc = await io.read(process.argv[2]);
const root = doc.getRoot();
const joints = root.listSkins()[0].listJoints();
const isBody = joints.map((n) => n.getName().startsWith('Spine'));
const mesh = root.listMeshes()[0];
const prim = mesh.listPrimitives().reduce((a, b) => (b.getIndices().getCount() > a.getIndices().getCount() ? b : a));
const pos = prim.getAttribute('POSITION');
const jA = prim.getAttribute('JOINTS_0'), wA = prim.getAttribute('WEIGHTS_0');
const n = pos.getCount();
const p = [0, 0, 0], j = [0, 0, 0, 0], w = [0, 0, 0, 0];
// bucket body vertices by Z slab from nose (+Z) backward; track Y min/max per slab
const slabs = {};
let bodyMinY = 1e9, bodyMaxY = -1e9;
const front = []; // {x,y,z} of front-most body verts
for (let i = 0; i < n; i++) {
jA.getElement(i, j); wA.getElement(i, w);
let best = 0, bw = -1; for (let k = 0; k < 4; k++) if (w[k] > bw) { bw = w[k]; best = j[k]; }
if (!isBody[best]) continue;
pos.getElement(i, p);
bodyMinY = Math.min(bodyMinY, p[1]); bodyMaxY = Math.max(bodyMaxY, p[1]);
const slab = Math.round(p[2] * 10) / 10;
const e = slabs[slab] || (slabs[slab] = { c: 0, ymin: 1e9, ymax: -1e9 });
e.c++; e.ymin = Math.min(e.ymin, p[1]); e.ymax = Math.max(e.ymax, p[1]);
if (p[2] > 0.30) front.push([p[0], p[1], p[2]]);
}
console.log(`body Y range overall: ${bodyMinY.toFixed(3)} .. ${bodyMaxY.toFixed(3)}\n`);
console.log('Z slab (nose=+Z → tail) bodyY min..max (height)');
for (const z of Object.keys(slabs).map(Number).sort((a, b) => b - a)) {
const e = slabs[z];
console.log(` z=${z.toFixed(1).padStart(5)} n=${String(e.c).padStart(4)} Y ${e.ymin.toFixed(3)} .. ${e.ymax.toFixed(3)} (${(e.ymax - e.ymin).toFixed(3)})`);
}
// the lowest front verts (potential lower lip / jaw)
front.sort((a, b) => a[1] - b[1]);
console.log('\nlowest 6 front (z>0.30) body verts [x, y, z]:');
for (const v of front.slice(0, 6)) console.log(' ', v.map((x) => x.toFixed(3).padStart(7)).join(', '));
console.log('frontmost 6 (max z) body verts [x, y, z]:');
front.sort((a, b) => b[2] - a[2]);
for (const v of front.slice(0, 6)) console.log(' ', v.map((x) => x.toFixed(3).padStart(7)).join(', '));