Skip to content

feat(site): rebuild homepage as indigo scroll story#40

Open
drewstone wants to merge 21 commits into
masterfrom
feat/homepage-indigo-story
Open

feat(site): rebuild homepage as indigo scroll story#40
drewstone wants to merge 21 commits into
masterfrom
feat/homepage-indigo-story

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Rebuilds the homepage as a single-hue indigo scroll story.

What changed

  • Dispersion palette retired everywhere (hero text, knot shaders, trace lines, waterfall spans, chips, footer): single indigo luminance ramp #C7C9F5 → #818CF8 → #6366F1 → #4F46E5, neutrals, semantic red/green only.
  • New outline: WebGL knot hero (fresnel shader + bloom, poster fallback) → proof strip (real router numbers) → harness chapter (8 corrected marks, scroll-driven trace convergence) → Intelligence split-scroll: session-trace waterfall → analyst finding → ship-stage deal-out (cards land tilted, scrubbed by scroll; tunable via the CHOREO config at the top of the page script) → indigo product cards (new generated art) → open source section → pay-as-you-go pricing (copy sourced from live router site, links id.tangle.tools) → closing CTA.
  • Internal review surfaces (noindex, unlinked): /art-review, /ideas-review, /knot-lab.
  • Adds three; static fallbacks for reduced-motion, no-JS, and narrow viewports throughout.

Tuning openings

  • Deal-out feel: CHOREO + STAGE2_START in index.astro script (per-card timing, tilt, entry).
  • Stage pacing: .intel-flow height + .intel-step offsets.
  • Art: swap files in public/images/brand/{cards,picks}; briefs archived in gtm assets.

Screenshots in ~/company/gtm/execution-plans/assets/tangle-website-rebuild/ (home-v5-desktop/mobile, deal-mid/full).

drewstone added 2 commits July 2, 2026 06:55
Single-hue indigo system replaces the dispersion palette across every
surface. New homepage outline: WebGL knot hero with poster fallback,
proof strip from live router numbers, harness chapter with scroll-driven
trace convergence, Intelligence split-scroll (session-trace waterfall,
analyst finding, ship-stage deal-out with tunable CHOREO config),
indigo product-card art, open source section, pay-as-you-go pricing
sourced from live router copy, closing CTA, dispersion-free footer.

Internal review surfaces (noindex): /art-review, /ideas-review,
/knot-lab. Harness marks corrected (Claude Code spark, OpenCode
favicon, Hermes product mascot, designed Pi monogram). Adds three.js;
hero renders a fresnel-shader torus knot with bloom, static fallbacks
for reduced-motion, no-JS, and mobile.
…o-story

# Conflicts:
#	src/pages/blog/index.astro
#	src/pages/research.astro

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — c05933fb

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-02T10:58:49Z

@tangletools tangletools 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.

🟠 Value Audit — better-approach-exists

Verdict better-approach-exists
Concerns 5 (3 medium-concern, 2 weak-concern)
Heuristic 0.0s
Duplication 0.1s
Interrogation 260.9s (2 bridge agents)
Total 261.0s

💰 Value — better-approach-exists

Replaces the sparse homepage with a scroll-driven indigo narrative (WebGL hero, proof strip, harness rail, trace waterfall, deal-out ship cards, product cards, OSS, pricing); the core direction is sound, but it should reuse the existing StoryScroller component and shed dead CSS and internal-review p

  • What it does: Rebuilt src/pages/index.astro from a single hero-video page (~89 removed lines) into a 2,242-line scroll story: WebGL torus-knot hero with fresnel shader and bloom, proof strip, scroll-activated harness rail with SVG trace lines, pinned Intelligence section showing trace waterfall → analyst finding → deal-out ship cards, product cards, open-source section, pay-as-you-go pricing, and closing CTA. A
  • Goals it achieves: Makes the marketing homepage tell Tangle's product story end-to-end (sandbox → trace → intelligence → ship) with a single indigo brand system, real router numbers, and concrete proof points. Unifies visual language around one hue family and gives buyers a clear path from value prop to pricing to API key.
  • Assessment: Good on its merits for the homepage itself: coherent narrative, real data, accessible fallbacks (reduced motion, narrow viewport, static stack), and tighter messaging than the old single-hero page. The indigo-only palette and ship-stage choreography are intentional product-design choices, not code flaws.
  • Better / existing approach: The codebase already has src/components/ui/StoryScroller.astro, a pinned-scroll narrative component built for exactly the intelligence-stage pattern, yet the PR hand-rolls a duplicate inside index.astro. I also found ~375 lines of dead CSS in index.astro for an earlier iteration (classes .intel-head, .stage-nav, .sim-card, .analyst-card, .pr-card, etc. have no matching markup at src/pages/index.as
  • Model: opencode/kimi-for-coding/k2p7
  • Bridge attempts: 1

🎯 Usefulness — sound-with-nits

A coherent, well-integrated homepage rebuild that is the site's primary entry surface — everything is reachable as intended and the headline claims are backed by real data; only implementation-layer nits (dead CSS, an incomplete no-JS fallback) remain, which separate review passes own.

  • Integration: index.astro is the homepage at / — the most reachable surface in the site — wrapped in the existing BaseLayout and Header/Footer. /brand-kit (527 lines, complete) is linked from the new Footer company column (src/components/Footer.astro:31). /research was intentionally retired to a 301 redirect to a blog series (src/pages/research.astro:1-7), so old links resolve rather than 404 — not dead s
  • Fit with existing patterns: Fits the grain. This is a static Astro marketing site; the homepage was previously a simpler page, so this is a rebuild (not a parallel implementation competing with an established pattern). Adding three (^0.185.1) for a WebGL hero is the right tool and there is no pre-existing WebGL/animation pattern to duplicate or collide with — the old hero was static/video. The new indigo design tokens (`--
  • Real-world viability: Strong happy-path and degraded-motion coverage: the WebGL hero is gated behind !reduced && wide (index.astro:423) with a poster fallback, an IntersectionObserver pauses the render loop when offscreen (line 492), and reduced-motion/narrow screens get .static layouts for both the sandbox and intelligence chapters. The proof-strip headline is grounded: src/data/router-models.json reports `count:
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

💰 Value Audit

🟠 Scroll-driven Intelligence section reinvents existing StoryScroller [duplication] ``

src/components/ui/StoryScroller.astro already implements a pinned, scroll-driven, stage-switching narrative with sticky container, stage panels, and mobile fallback. The PR adds a parallel implementation in src/pages/index.astro:204-304 and :1081-1178 instead of composing with/extendeding that component. Better approach: use StoryScroller for the trace→analyze→ship narrative, or update StoryScroller if the new layout needs a different panel arrangement.

🟠 index.astro ships ~375 lines of dead CSS [maintenance] ``

src/pages/index.astro:1618-1993 contains CSS for classes (.intel-head, .stage-nav, .stage-area, .sim-card, .analyst-card, .pr-card, .pr-diff, etc.) that do not appear in the page markup or script. These are leftovers from an earlier iteration of the intelligence section and will confuse future edits. Better approach: delete this block before merge.

🟠 Internal review pages and assets bloat the production build [proportion] ``

The PR adds /art-review, /ideas-review, and /knot-lab (src/pages/art-review.astro:1, src/pages/ideas-review.astro:1, src/pages/knot-lab.astro:1) plus src/data/art-review.json, src/data/briefs-v2.mjs, src/data/briefs-v3.mjs, src/data/router-models.json, and public/images/art-review (~2.2 MB). These are explicitly unlinked/noindex review surfaces but still ship in every deploy. Better approach: keep review artifacts in ~/company/gtm or a non-production branch; do not bundle them into the public si

🟡 Unused 3.1 MB hero-loop.mp4 is added [proportion] ``

public/videos/hero-loop.mp4 is included in the commit but not referenced anywhere in the new homepage or other pages (grep for hero-loop returns no Astro/HTML/CSS/JS hits). The WebGL hero uses /images/brand/knot-poster.webp as its fallback. Better approach: remove the unused video.

🟡 New homepage mostly ignores canonical ui/ primitives [against-grain] ``

The codebase has src/components/ui/Section.astro, CtaBlock.astro, FeatureCard.astro, BentoCell.astro, and Card.astro to replace hand-rolled sections, yet index.astro defines its own .shell layout, buttons, product cards, pricing steps, and footer-style CTA. Some of this is justified by the bespoke scroll behavior, but it creates more one-off styles to maintain. Better approach: where the layout is standard (proof strip, product cards, pricing, closing CTA), compose from the existing primitives.


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260702T113231Z

@tangletools

Copy link
Copy Markdown
Contributor

⚠️ Review Interrupted — c05933fb

The review runner stopped before publishing a final verdict: max_run_seconds.

State Detail
Interrupted max run seconds

No review verdict was produced for this run. Trigger a fresh review on the current PR head if the PR is still open.

tangletools · #40 · model: kimi-for-coding · updated 2026-07-02T13:27:50Z

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — c05933fb

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-02T13:47:17Z

@tangletools tangletools 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.

🟠 Value Audit — better-approach-exists

Verdict better-approach-exists
Concerns 5 (1 strong-concern, 2 medium-concern, 2 weak-concern)
Heuristic 0.0s
Duplication 0.1s
Interrogation 276.6s (2 bridge agents)
Total 276.7s

💰 Value — better-approach-exists

A coherent visual and narrative overhaul of the homepage that is implemented as a 2,242-line monolithic Astro file full of dead CSS, ignoring the repo's own extracted component primitives and duplicating WebGL code.

  • What it does: Rebuilds the Tangle homepage as a single-hue indigo scroll story: a WebGL torus-knot hero (Three.js fresnel shader + bloom, with poster/reduced-motion/no-JS fallbacks), a proof strip, a scroll-driven sandbox harness chapter, an Intelligence split-scroll story (trace waterfall → analyst finding → deal-out ship-stage), product cards, open-source section, pay-as-you-go pricing, and a rewritten footer
  • Goals it achieves: Unify the site under one indigo material palette; replace the previous sparse hero with a concrete product story that shows sandbox → trace → analysis → ship; surface real proof points (477+ models, 8 harnesses); and guide visitors to the console, docs, and API key signup. It also retires warm paper, lowers display weights to 600, and documents the new brand system in /brand-kit.
  • Assessment: The direction is good: the story maps to the actual product loop, the numbers are sourced from live router data, and the indigo system is a real improvement over the previous dispersed palette. But the implementation is unnecessarily monolithic. The homepage file grew from 188 lines to 2,242 lines (src/pages/index.astro:1), mixing markup, ~200 lines of inline Three.js, and ~1,600 lines of inline C
  • Better / existing approach: The right architecture is to compose the page from existing and new components instead of inlining everything. Existing primitives should be reused or extended: Section.astro for page sections, Hero.astro or a new KnotHero component for the WebGL hero, Button.astro for CTAs, StatsStrip.astro for the proof strip, FeatureCard.astro/Card.astro for product cards, and CtaBlock.astro for the closing CTA
  • Model: opencode/kimi-for-coding/k2p7
  • Bridge attempts: 1

🎯 Usefulness — sound-with-nits

A coherent, well-grounded rebuild of the homepage as a single-hue scroll story that wires in cleanly, ships correct fallbacks, and leaves intentional internal-review surfaces — no dead or competing surface.

  • Integration: Fully reachable. The homepage is the root route, linked from Header.astro:11; /brand-kit is linked from Footer.astro:16. The three review pages (art-review, ideas-review, knot-lab) are deliberately unlinked + noindex with stated internal purpose (art ranking, interaction prototypes, WebGL knot lab) and each consumes its data: art-review.astro:4-6 reads art-review.json + briefs-v2/v3.mjs; ideas-rev
  • Fit with existing patterns: Fits the grain. Replaces a thin 188-line master homepage with a richer version using the existing BaseLayout, CSS vars (--font-mono/--font-display), and the established .reveal IntersectionObserver pattern. Adds three as a dependency — appropriate and the only sane way to get the WebGL knot; poster + reduced-motion + narrow-viewport fallbacks mean the dependency degrades cleanly rather than bl
  • Real-world viability: Robust on the realistic paths that matter for a marketing page. Hero WebGL only initializes when !reduced && wide (index.astro:423), else falls back to static poster and a static-hero class; rendering pauses when the canvas leaves the viewport (IntersectionObserver, :492); sizing via ResizeObserver (:484). Both scroll choreographies rAF-throttle passive scroll listeners and short-circuit to a `.
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🎯 Usefulness Audit

🟡 Homepage hardcodes the proof numbers instead of reading the snapshot it ships [robustness] ``

index.astro:145-146 hardcodes '477+' and '61+' providers, and :86 hardcodes '477+'. The PR adds src/data/router-models.json (count:477, fetched 2026-07-01) but the homepage never imports it — only ideas-review.astro:5 does. The headline proof will silently drift from the data file the day a model is added, and '61+ providers' has no backing field in the JSON at all (it only carries ids). Minor available improvement: derive the model count from the JSON (and snapshot a provider count) so the proo

💰 Value Audit

🔴 Homepage is a 2,242-line monolith that ignores existing UI primitives [against-grain] ``

src/pages/index.astro grew from 188 lines to 2,242 lines, with all markup, ~200 lines of Three.js, and ~1,600 lines of CSS inline. The repo has already extracted Section.astro (replacing hand-rolled wf-section patterns 36×), Hero.astro (replacing 8 bespoke hero blocks), CtaBlock.astro, Button.astro, StatsStrip.astro, FeatureCard.astro, and Card.astro. The new homepage defines its own buttons (.btn, .btn-primary), sections, hero, stats/proof strip, product cards, and CTA instead of reusing or ext

🟠 WebGL knot shader is duplicated between homepage and knot-lab [duplication] ``

The fresnel dispersion shader, bloom setup, and render loop appear nearly identically in src/pages/index.astro:411-510 and src/pages/knot-lab.astro:26-172. The better approach is a shared KnotHero.astro component that accepts props (camera offset, drag-or-parallax behavior, reduced-motion handling) and is reused by both pages.

🟠 Dead CSS for abandoned stage variants remains in the file [maintenance] ``

The CSS block at src/pages/index.astro:1664-1993 defines .stage-nav, .sim-card, .analyst-card, .pr-card, and related rules, but grep finds no matching HTML class attributes in the file. These appear to be leftovers from earlier iterations of the Intelligence story. Dead rules in a 2,242-line file make the page harder to maintain and review.

🟡 Internal review pages add ~835 lines of unlinked/noindex code [proportion] ``

src/pages/art-review.astro, ideas-review.astro, and knot-lab.astro are noindex and unlinked, but ideas-review.astro alone is 470 lines of live prototypes. They are useful for tuning, but their scope could be trimmed—especially ideas-review, whose non-shipped experiments could live in gtm assets rather than in the deployable site.


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260702T135300Z

@tangletools

Copy link
Copy Markdown
Contributor

✅ No Blockers — c05933fb

Review health 100/100 · Reviewer score 31/100 · Confidence 95/100 · 26 findings (4 medium, 22 low)

opencode-kimi glm aggregate
Readiness 53 31 31
Confidence 95 95 95
Correctness 53 31 31
Security 53 31 31
Testing 53 31 31
Architecture 53 31 31

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 8/8 planned shots over 85 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 85 changed files. Global verifier still owns final merge decision.

🟠 MEDIUM Mobile CTA label inconsistent with desktop — src/components/Header.astro

The desktop CTA was updated to 'Open console' (line 22) but the mobile menu still shows 'Launch Agent' (line 40). Same href, same component, different labels will confuse mobile users and looks unfinished.

🟠 MEDIUM router-models.json breaks deprecated-model CI check — src/data/router-models.json

pnpm check:models treats .json under src/ as invocation config and scans src/data/router-models.json. The file is a display catalog (used by ideas-review.astro idea #8), but the scanner flags numerous IDs as deprecated (e.g. gpt-5-codex, o3-mini, gemini-3.5-flash) and exits 1. Reproduced locally: the output lists src/data/router-models.json findings and the script fails. Fix: add src/data/router-models.json to .deprecated-models.allow.json files array, or adjust scripts/check-models.mjs to skip src/data/*.json catalog files.

🟠 MEDIUM index.astro contains extensive dead and duplicated CSS — src/pages/index.astro

The stylesheet contains large duplicated blocks (products/sandbox/intelligence CSS appears twice at lines ~830 and ~1360) and entire dead sections for an earlier markup iteration: .stage-nav, .stage-area, .stage-0/1/2 (lines ~1612+), .sim-card/.trace-list/.analyst-card (lines ~1745+), and .pr-card/.pr-actions (lines ~1839+). None of these selectors match the current HTML, which uses .istage/.istage-0/1/2 and .wf-card/.ann-card instead. This is not a runtime bug, but it materially bloats the file (style block is ~1630 lines in a 2243-line file), makes future edits error-prone, and indicates cleanup was not finished. Fix: delete the unused .stage-*, .sim-card/.trace-list/.analyst-card, and .pr-card blocks; consolidate the duplicate products/sandbox/intelligence blocks into single definitions

🟠 MEDIUM index.astro ships ~650 lines of duplicate + orphaned CSS, with a conflicting .intel-flow height — src/pages/index.astro

The <style> block defines the same section selectors twice. .products-section appears at line 830 and again verbatim at line 1360; same for .statement, .statement-grad, .product-cards, .p-card/.p-card-art/.p-card-body, .sb-section, .sb-flow, .sb-tile, and .intel-section/.intel-flow/.intel-sticky. The second block (lines ~1357-2010) is a mix of (a) exact duplicates of the first and (b) fully orphaned selectors whose classes NEVER appear in the markup: .stage-nav, .stage/.stage-0/1/2, .stage-area, .sim-card, `

🟡 LOW Five SVGs missing trailing newline (No newline at end of file) — public/images/harness/claude-code.svg

git diff reports '\ No newline at end of file' for claude-code.svg, opencode.svg, github.svg, linear.svg, slack.svg (pi.svg is fine). Purely cosmetic; some linters/CI formatters flag it. Add a single \n at EOF to match pi.svg and the repo's other text assets.

🟡 LOW opencode.svg carries a no-op prefers-color-scheme style block — public/images/harness/opencode.svg

The trailing <style> sets filter: none in BOTH light and dark media-query branches — i.e. it never changes anything. Either remove the block or give it a real purpose (e.g. invert/dim for dark mode). Harmless but dead CSS inside a shipped asset.

🟡 LOW Two product images added but referenced nowhere in src/ — public/images/product/sandbox-full.webp

sandbox-full.webp (137472 B, 2556x1758) and sandbox-workspace.webp (62474 B, 1404x934) are committed but grep across src/ (.astro/.mdx/.md/.ts/.tsx/.json) returns zero references; the full-PR diff to src/pages/index.astro does not mention either path. Net ~200KB of unreferenced bundle weight today. Either wire them into the sandbox product section or drop them until the consuming component lands. Not blocking — files are valid and inert.

🟡 LOW Unused product screenshots and inconsistent directory naming — public/images/product/sandbox-full.webp

sandbox-full.webp and public/images/product/sandbox-workspace.webp are added but no source file references them (grep for 'sandbox-full', 'sandbox-workspace', and 'images/product' in src/ returns nothing). They are also under public/images/product/ (singular), whereas existing product screenshots are under public/images/products/ (plural). Either wire them into the intended page/CSS or remove them to avoid dead weight.

🟡 LOW Product 'hue' field is uniform, undermining the per-product hue intent — src/components/Footer.astro

All three products share hue: '#818CF8' (Sandbox, Intelligence, Inference), so style={color: ${p.hue}} renders identically for every product. The component is wired to support per-product color but the data doesn't deliver it. Impact: visual only; the dispersion/hairline gradient is unaffected. Fix: either assign distinct hues per product or drop the hue field and hard-code the single color to remove dead configurability.

🟡 LOW Redundant/conflicting border declarations on .ft-console — src/components/Footer.astro

The rule sets border-bottom: 1px solid transparent; then border-image: linear-gradient(...) 1; then re-asserts border-bottom-width: 1px; border-bottom-style: solid;. When border-image is set it paints over the declared border-color/width/style, so the transparent + width/style re-assertions are dead. Cosmetic only, no functional impact. Fix: keep just border-bottom: 1px solid; border-image: linear-gradient(90deg, #A5AAFC, #6366F1, #4F46E5) 1; and drop the redundant lines.

🟡 LOW Home link falsely claims current page everywhere — src/components/Header.astro

<a href='/' aria-current='page' ...> is hardcoded, so screen readers will announce the home link as the current page on every route. Pre-existing but in the shot file; should be conditional on Astro.url.pathname === '/'.

🟡 LOW Mobile menu CTA text diverges from desktop after rename — src/components/Header.astro

The diff renamed the desktop CTA from 'Launch Agent' to 'Open console' at line 22 but left the duplicate mobile-menu CTA at line 40 as 'Launch Agent'. grep confirms these are the only two occurrences in src/ and they now disagree. Impact: inconsistent label for the same destination (https://ai.tangle.tools) between desktop and mobile breakpoints, and the renamed wording never reaches mobile users. Fix: update Header.astro:40 to read 'Open console' to match [line 22](https://github.com/tangle-network/tangle-website/blob/c05933fba561844

🟡 LOW VIDEO_MOTION exported but never imported — src/data/briefs-v2.mjs

grep finds VIDEO_MOTION only at its definition; art-review.astro imports BRAND_CORE, HERO_FIELD_V2, CARD_SANDBOX, CARD_INTELLIGENCE, CARD_INFERENCE from v2 — VIDEO_MOTION is dead. Not a bug (exports are a public surface), but either wire it into the gallery or drop it to keep the module honest. Impact: none at runtime.

🟡 LOW Denormalized count field can silently drift from ids.length — src/data/router-models.json

"count": 477 is hand-maintained alongside the 477-entry ids array. Today they agree (verified: ids.length===477). If a future edit appends/removes an ID without updating count, consumers trusting count (e.g. ideas-review.astro copy '477-model snapshot') will lie. Fix: derive the displayed number from ids.length at the call site, or assert count===ids.length in the consumer, rather than storing both.

🟡 LOW No trailing newline — src/data/router-models.json

File ends with ] and no newline (diff shows '\ No newline at end of file'). POSIX/repo-lint convention prefers a final newline; some formatters/prettier configs flag it. Trivial fix, no runtime impact on JSON.parse.

🟡 LOW Snapshot labeled 'live' in consumer but is a hardcoded static fetch — src/data/router-models.json

"fetched": "2026-07-01" pins this to a single day; ideas-review.astro idea #8 copy says 'IDs are from the live catalog snapshot' and 'real model IDs from the live catalog'. The data is a stale-by-construction snapshot that will drift from the real router. Acceptable for an internal noindex prototype page, but the word 'live' overstates it — consider 'snapshot dated 2026-07-01' in the page copy so the gallery doesn't mislead during a future review.

🟡 LOW Duplicate @media (max-width: 640px) blocks in BlogLayout — src/layouts/BlogLayout.astro

The new table-scroll media query (lines 418-432) and the pre-existing hero/article media query (lines 445-484) share the same max-width: 640px breakpoint. Functionally correct (both apply), but they could be merged into one block for maintainability. Pure style nit — no behavioral impact.

🟡 LOW Homepage hero WebGLRenderer creation is unguarded; a context-creation throw leaves a blank hero with no poster fallback — src/pages/index.astro

new THREE.WebGLRenderer({ canvas: knotCanvas, antialias: true }) (line 426) throws when WebGL is unavailable/blocked (some corporate proxies, locked-down browsers, headless crawlers with no GPU). The static-hero fallback (document.documentElement.classList.add('static-hero') at line 511) only runs in the else branch of if (knotCanvas && !reduced && wide) — it is NOT reached when the renderer throws. Result: on WebGL failure the <canvas> stays at opacity:0 (its default) AND .hero-poster stays display:none, so the hero shows only

🟡 LOW knot-lab.astro rAF loop never pauses when the page is off-screen or backgrounded — src/pages/knot-lab.astro

const frame = () => { ... composer.render(); requestAnimationFrame(frame); } runs unconditionally with no visibility gate. The homepage hero correctly uses an IntersectionObserver to skip composer.render() when !visible (index.astro line 499), but knot-lab.astro omits this. Impact: continuous GPU/composer work while the tab is backgrounded or scrolled away. Internal noindex page so impact is limited, but it is an avoidable battery/CPU drain. Fix: add the same IntersectionObserver-on-canvas visibility gate used in the homepage.

🟡 LOW research.astro 301 is emitted as a meta-refresh, not a real 301, in static output mode — src/pages/research.astro

return Astro.redirect('/blog/series/the-self-improving-stack', 301) runs under output: 'static' (astro.config.mjs:12). In static mode Astro ignores the status code and generates an HTML redirect page with <meta http-equiv="refresh"> plus a canonical link, which search engines treat as a soft redirect (not a true 301). Functionally correct for users and the target resolves (verified), but if a hard 301 matters for SEO link equity, configure redirects in astro.config.mjs (supported for static adapters) or move the redirect to the host/edge. Acceptable as-is for a dormant route; flagging only because the literal 301 in source is not what ships.

🟡 LOW --depth-0 and --depth-5 are defined but never consumed — src/styles/global.css

git grep for 'var(--depth-0)' and 'var(--depth-5)' across the entire repo (css + astro + ts) returns ZERO matches. These two new tokens are dead weight — either wire them into the surfaces the comments describe ('wells, page edges, behind hero art' / 'active/pressed, highest lift') or drop them until a consumer exists. Impact: premature abstraction, future readers will assume the ladder is in use. Fix: remove the two lines, or migrate at least one call site (e.g. html/body background-color: var(--depth-1) → var(--depth-0)) in this same PR.

🟡 LOW --font-display fallback chain weaker than --font-sans — src/styles/global.css

--font-display is now '"Inter", sans-serif' while --font-sans keeps '"Inter", "DM Sans", ui-sans-serif, system-ui, sans-serif'. If Inter's @font-face fails to load, headings drop straight to generic sans-serif while body text renders system-ui — inconsistent fallback behavior between the two stacks. Minor brand robustness nit; fix by mirroring the system-ui fallback: '--font-display: "Inter", ui-sans-serif, system-ui, sans-serif;'.

🟡 LOW .wf-h1 line-height source-of-truth split between hardcoded and token — src/styles/global.css

Base .wf-h1 sets 'line-height: 0.95;' (hardcoded literal, changed from 0.9) while the media-query override at line 3322 uses 'line-height: var(--type-h1-line) !important;' (= 1.02). The two values come from different sources and the jump (0.95 → 1.02 at the breakpoint) is larger than the h1 token itself prescribes (1.02). Either set the base to var(--type-h1-line) too, or document why the large-screen override relaxes leading. Pre-existing pattern of hardcoded-vs-token split, but this PR widens it by converting sibling props (weight, tracking) to tokens while leaving line-height literal.

🟡 LOW Comment overstates ladder completeness; --depth-1..4 still undefined — src/styles/global.css

The header comment reads 'Full contrast-checked ladder' and the file references --depth-1 (lines 352, 360, 2287), --depth-2 (2428, 2482, 2877), --depth-3 (2340, 2530, 2582, 2849, 2927, 2940, 2997, 3074, 3285), --depth-4 (2347, 2541, 2591, 2934, 3298, 3481) — none of which are defined in this file, fonts.css, or any tailwind/astro config at HEAD. This is pre-existing (base commit has the same gap), so NOT a regression introduced here, but the new 'full ladder' comment is misleading about what this PR delivers. Fix: either define the missing rungs as part of the indigo ladder, or reword the comment to 'adds the two missing poles (--depth-0/--depth-5); mid-rungs --depth

🟡 LOW Duplicate root declarations shadow earlier token values — src/styles/global.css

Within the same :root block --editorial-paper is first set to #f4f4f9 at line 226 and then overwritten to var(--paper-0) at line 250; --editorial-nav-bg is first set to rgba(12, 11, 29, 0.88) at line 230 and overwritten to rgba(17, 18, 45, 0.88) at line 252. Because both declarations t

🟡 LOW Updated .wf-h1/.wf-h3 rules are overridden by later !important reset — src/styles/global.css

The diff updates .wf-h1 (line ~2192) and .wf-h3 (line ~2203) to use the new type tokens, but both are later overridden by the foundation reset at lines 3319 and 3350, which applies !important to font-size, font-weight, letter-spacing, and line-height. The weight/tracking still align because the reset consumes the same CSS variables, but the font-size and line-height values in the earlier blocks are dead code, so edits there are no-ops. Either remove the redundant earlier overrides or consolidate the display scale in one place.


tangletools · 2026-07-02T14:19:31Z · trace

tangletools
tangletools previously approved these changes Jul 2, 2026

@tangletools tangletools 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.

✅ Approved — 26 non-blocking findings — c05933fb

Full multi-shot audit completed 8/8 planned shots over 85 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 85 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-02T14:19:31Z · immutable trace

Brand kit page moves to the single-hue indigo ladder and the indigo knot
atmosphere; usage rules state the one-hue law. Removes the raw router
catalog snapshot (the deleted model wall's dependency) so the deprecated-
models CI check passes; the ideas-review constellation orbits provider
names instead of model IDs. Strips dead PR-card CSS and recolors the
stage-nav underline to the indigo ramp.

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — ada4d040

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-02T19:23:48Z

@tangletools tangletools 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.

🟠 Value Audit — better-approach-exists

Verdict better-approach-exists
Concerns 5 (3 medium-concern, 2 weak-concern)
Heuristic 0.0s
Duplication 0.1s
Interrogation 174.7s (2 bridge agents)
Total 174.8s

💰 Value — better-approach-exists

Rebuilds the Tangle homepage as a long-form indigo scroll story with a WebGL knot hero, scroll-driven stages, and new brand surfaces; the direction is coherent, but it should reuse the codebase's existing UI primitives and shed dead CSS/assets before merging.

  • What it does: Replaces the single-hero video homepage with a multi-section narrative page: a WebGL torus-knot hero, a proof strip, a scroll-driven 'harnesses → traces' sandbox chapter, a three-stage Intelligence story with deal-out ship cards, product cards, an open-source section, pay-as-you-go pricing, and a refreshed footer. It also adds internal noindex review pages (/art-review, /ideas-review, /knot-lab),
  • Goals it achieves: Unify the marketing homepage around the new indigo-material brand system; tell the end-to-end product story (run agents in sandboxes, trace every run, analyze with intelligence, ship improvements); replace the old sparse hero page; and create internal surfaces for art/interaction iteration.
  • Assessment: The narrative and visual direction are coherent, and the page includes thoughtful accessibility fallbacks (prefers-reduced-motion, no-JS, narrow viewport). However, it lands as a ~2,000-line monolithic page that hand-rolls sections, headings, buttons, cards, scroll stages, and CTAs that the codebase already shares via components. That duplicates existing capability, ignores the global type/spacing
  • Better / existing approach: Extend the existing shared primitives instead of inlining them: use Section, Hero, SectionHead, Button, CtaBlock, and StoryScroller (src/components/ui/Section.astro:1, Hero.astro:1, StoryScroller.astro:1) for the page blocks and scroll stages; consume the global --type-h* / --section-pad-* tokens in global.css:267-308. Extract the WebGL knot into a reusable component shared by the hero and /knot-l
  • Model: opencode/kimi-for-coding/k2p7
  • Bridge attempts: 1

🎯 Usefulness — sound-with-nits

A coherent, well-wired homepage rebuild with proper fallbacks; the only real issue is ~80 lines of orphaned CSS left behind in the footer class-namespace migration.

  • Integration: Fully reachable. Footer.astro is consumed by BaseLayout.astro:107 so every page gets it. index.astro is the homepage at /. The three dependency is imported client-side via <script> at index.astro:412-415, matching the same pattern already used in knot-lab.astro:27-30 — no SSR concern since output is static. Pricing CTAs link to id.tangle.tools (index.astro:375-403) and product cards to san
  • Fit with existing patterns: Mostly in-grain. The new Footer.astro introduces a fresh .ft-* namespace with its own scoped <style> block (Footer.astro:79) instead of reusing the repo's pervasive wf-* (Webflow) classes. Header.astro still emits wf-nav/wf-*, so this is a deliberate half-migration of one component, not a competing pattern. The inline <script> approach for the WebGL/choreo logic is the same pattern kno
  • Real-world viability: Solid on edge paths. The WebGL hero is gated behind !reduced && wide (index.astro:423) with knot-poster.webp fallback; an IntersectionObserver pauses the render loop when off-screen (index.astro:492,497); both scroll-driven chapters fall back to a .static stack under 861px or reduced-motion (index.astro:513-514, 532, 553-554). Tunable knobs (CHOREO, STAGE2_START) are centralized and document
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🎯 Usefulness Audit

🟡 Old .wf-footer CSS orphaned in global.css after footer rewrite [problem-fit] ``

Footer.astro now emits only .ft-* classes with scoped styles, but src/styles/global.css still carries ~42 references to .wf-footer* across lines ~1485-1670, 1756, 1894-1909, 2876-2921, 3216-3217 (logo, grid, tagline, links, legal, social, responsive overrides). These selectors no longer match any markup in the build. Not a breakage — the new footer is self-contained — but it's dead weight that will confuse future editors and bloat the stylesheet. Recommend deleting the orphaned `.wf-footer

💰 Value Audit

🟠 Homepage re-implements shared UI primitives instead of extending them [duplication] ``

The repo already has canonical components for exactly these surfaces — Section, Hero, SectionHead, Button, CtaBlock, Card, and StoryScroller (src/components/ui/*.astro) — plus global type/spacing tokens (global.css:267-308). index.astro ignores them and hand-rolls every section, button, heading scale, card, and scroll-stage with ~2,000 lines of page-scoped CSS/JS. That duplicates existing capability and will cause the homepage to drift from the rest of the site's design system.

🟠 Page ships duplicate and unused CSS [maintenance] ``

index.astro contains two identical .products-section blocks (lines 829 and 1359), a second .stage-nav/.stage/.sim-card/.trace-list block (lines 1611-1742) that is not used by the current markup (the live markup uses .istage/.wf-card/.ship-card), and a responsive rule for .harness-grid (line 2062) that has no matching class in the file. This is dead weight that should be removed before merge.

🟡 WebGL knot setup is duplicated between the hero and /knot-lab [duplication] ``

The hero script (index.astro:411-510) and the knot-lab page (knot-lab.astro:26-172) both instantiate a Three.js scene, camera, TorusKnotGeometry, fresnel shader, halo mesh, bloom composer, ResizeObserver, and pointer parallax. Better: extract a WebGLKnot.astro component used by both, so fixes/tuning happen in one place.

🟠 Large asset dump includes many unused deployed files [proportion] ``

public/videos/hero-loop.mp4, public/images/brand/picks/, public/images/product/, and most public/images/brand/atmospheres/* (only tangle-atmosphere-indigo-01.webp and tangle-paper-indigo-01.webp are referenced) have no references in the built site (grep returned no matches). The 29 art-review images are only used on the noindex art-review.astro page. These should be pruned or kept outside the deployed public directory to avoid bloating the build.


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260702T193024Z

@tangletools

Copy link
Copy Markdown
Contributor

❌ Needs Work — ada4d040

Review health 100/100 · Reviewer score 16/100 · Confidence 95/100 · 31 findings (1 high, 4 medium, 26 low)

opencode-kimi glm aggregate
Readiness 31 16 16
Confidence 95 95 95
Correctness 31 16 16
Security 31 16 16
Testing 31 16 16
Architecture 31 16 16

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision.

Blocking

🔴 HIGH index.astro carries ~1000 lines of duplicate and orphaned CSS — src/pages/index.astro

Lines 829-1610 are duplicated at 1359-1838. (1) 'products (paper, gradient cards)' appears twice verbatim — compare 829-940 with 1359-1470 (same .products-section, .statement, .statement-grad, .product-cards, .p-card, .p-card-art, .p-card-cyan/magenta/indigo rules). (2) 'sandbox chapter (dark, scroll-driven)' appears twice verbatim — 941-1080 vs 1471-1610. (3) The 'intelligence scroll flow (paper)' block at 1611-1838 (~228 lines) is fully orphaned: it targets .stage-nav, .stage-area, .stage-0/1/2, .sim-card, .sim-head, .trace-list, .analyst-card, .sim-note — grep finds 25 CSS references and ZERO markup uses (the markup at 209-303 uses .istage/.icopy/.ipane/.wf-

Other

🟠 MEDIUM Desktop and mobile CTA labels are inconsistent — src/components/Header.astro

Line 22 sets the desktop CTA text to 'Open the console' / 'Open console', but line 40 hardcodes the identical mobile CTA as 'Launch Agent'. Because this PR changed the desktop label, the mobile label is now stale and will confuse users seeing different copy for the same https://ai.tangle.tools action. Fix: change line 40 text to 'Open console' to match the desktop CTA.

🟠 MEDIUM WebGL setup not fault-tolerant; failure breaks page JS — src/pages/index.astro

The three.js renderer/composer creation is not wrapped in try/catch. If WebGL context creation fails (GPU block, memory pressure, software renderer disabled), the script throws and the subsequent reveal animations and scroll-driven sandbox/intelligence effects never initialize. Wrap renderer setup in try/catch and fall back to static-hero/static classes so non-canvas behavior still works.

🟠 MEDIUM index.astro contains duplicated and dead CSS blocks — src/pages/index.astro

The stylesheet contains the .products-section block twice (lines ~830 and ~1359), the .sb-section/.sb-flow block twice (lines ~942 and ~1471), and a large .intel-flow 'scroll flow' block (lines ~1611-1837 with .stage, .stage-nav, .sim-card, .trace-list, .analyst-card) that has no matching markup. The rendered page uses .istage-*, .wf-*, and .ship-* instead. This bloats the bundle and obscures the real styles. Remove the duplicate and unused blocks.

🟠 MEDIUM sandboxSnippet defined but never rendered — src/pages/index.astro

const sandboxSnippet is declared and annotated as real SDK usage, but it is not referenced anywhere in the template. Matching .code-pane, .pane-head, .add, .del styles are also unused. Either render the snippet in the Sandboxes section or remove the dead const and styles.

🟡 LOW three.js adds significant client bundle weight — package.json

three@0.185.1 is a large dependency (~600KB minified, ~150KB gzipped). It is imported in src/pages/knot-lab.astro:27 and src/pages/index.astro:412. This is informational only — the cost is inherent to 3D rendering — but verify those pages code-split / lazy-load the three module so the homepage initial JS does not pay the full cost. No change required to package.json itself.

🟡 LOW Art-review candidate images served publicly despite 'internal' intent — public/images/art-review/01-hero-v2-01.webp

src/pages/art-review.astro:2 comments 'Internal art-review gallery. Unlinked, noindex.' but the ~15 candidate webps (hero-v2, card-sandbox, card-intelligence, card-inference, optimize-flow — including FLUX generations ~228KB each) live in public/images/art-review/ and are therefore directly fetchable by anyone who guesses the path; noindex only blocks search indexing, not access. Impact: low (internal review work product is public-readable) and bandwidth (~1.4MB for the in-shot art-review files alone). This appears to be a deliberate workflow choice, not an accident — flagging only so it's a conscious decision. Mitigation if unwanted: move candidates outside public/ and import via src/assets, or gate the route.

🟡 LOW Nested SVG with redundant CSS — public/images/harness/opencode.svg

The file wraps an inner inside an outer and appends a <style> block that sets filter:none for both light and dark schemes. This is valid but unusual; it adds unnecessary nesting and a no-op stylesheet. Flatten to a single root and remove the empty style block to make the asset smaller and less surprising.

🟡 LOW Text-based SVG relies on system fonts — public/images/harness/pi.svg

The pi glyph is rendered with . When the SVG is used as an image, the browser may substitute any available serif font if Georgia/Times New Roman are absent, causing slight visual inconsistency. Consider converting the glyph to a for deterministic rendering across environments.

🟡 LOW No git LFS for a growing binary corpus — public/images/product/sandbox-full.webp

Repo has no .gitattributes and git lfs track is empty, so all 65 binary images in this PR (~3MB, including 2556x1758 product shots and multiple ~250KB atmosphere/flux files) are committed directly to git history. Every future re-export doubles the stored history. Pre-existing repo convention (existing public/images already holds many large Webflow PNGs), so not a regression introduced here — but worth a one-time LFS adoption decision before the corpus grows further.

🟡 LOW Unreferenced product images add dead weight to deploy — public/images/product/sandbox-full.webp

rg -ni 'sandbox[-_]full|sandbox[-_]workspace' across the entire repo returns ZERO matches. Both public/images/product/sandbox-full.webp (137KB, 2556x1758) and public/images/product/sandbox-workspace.webp (62KB, 1404x934) are added but never referenced by any .astro/.tsx/.css/.json. They will ship to the production /images/ tree but never render. Impact: wasted deploy bytes + permanent git history bloat for unused assets. Fix: either wire them into the intended component (if a non-image file in this PR references them, confirm) or drop them before merge. Contrast: every other shot file (card-sandbox, harness svgs, logos, art-review/*) IS referenced.

🟡 LOW Unused product screenshots — public/images/product/sandbox-full.webp

No file in the repository references /images/product/sandbox-full.webp. It appears to be a checked-in asset with no caller. If it is intended for a future section, either wire it up or remove it to avoid public-dir bloat.

🟡 LOW Unused product screenshots — public/images/product/sandbox-workspace.webp

No file in the repository references /images/product/sandbox-workspace.webp. It appears to be a checked-in asset with no caller. If it is intended for a future section, either wire it up or remove it to avoid public-dir bloat.

🟡 LOW Comment claims 'three product hues' but all products share one hue — src/components/Footer.astro

Header comment: 'The hairline is the one place on the page where all three product hues sit on one continuous line.' But the products array (lines 5-7) sets hue: '#818CF8' identically for Sandbox, Intelligence, and Inference, and the rendered .ft-domain for all three uses that same color. The three-hue gradient only appears in .ft-hairline (#A5AAFC/#6366F1/#4F46E5), which has no per-product mapping. Comment misleads future editors into thinking each product owns a distinct hairline hue. Fix: either give each product a distinct hue or correct the comment.

🟡 LOW Footer logo link uses empty alt text — src/components/Footer.astro

<img src="/images/webflow/tangle icon.png" alt="" width="32" height="32" /> sits inside the home link <a href="/" class="ft-lockup"> whose only other content is the visible wordmark 'Tangle'. Empty alt on the lone image of a meaningful link is acceptable ONLY if adjacent text conveys the target; here the adjacent Tangle does, so screen readers still announce 'Tangle' — but the empty alt is borderline and the prior component had the same issue. Fix only if doing an a11y pass: alt="Tangle home" is more robust when the lockup is the page's primary brand link.

🟡 LOW Orphaned wf-footer-* CSS left in global.css — src/components/Footer.astro

Footer.astro was rewritten to use scoped ft-* classes, but src/styles/global.css still contains ~40 rules under .wf-footer, .wf-footer-inner, .wf-footer-grid, .wf-footer-brand, .wf-footer-link, .wf-footer-bottom, .wf-footer-legal-*, .wf-footer-social-* (lines 1485-3217, plus two .t-section--vault .wf-footer-tagline/.wf-footer-legal-link selectors at 152-153). No remaining emitter of any wf-footer-* class exists in src (rg confirmed). Impact: ~200 lines of dead CSS shipped to every page. Out of shot scope to delete (global.css not in this shot), but this change is what orphans it; flag for a follow-up cleanup commit.

🟡 LOW Redundant/contradictory border declarations on .ft-console — src/components/Footer.astro

border-bottom: 1px solid transparent; (141) is immediately followed by border-image: ... 1; (142), border-bottom-width: 1px; (143), border-bottom-style: solid; (144). The width/style/color on 141 are fully overridden by 143-144, and the transparent color is moot once border-image is set. Cosmetic dead declarations; works as intended. Fix: drop line 141 or collapse to border-bottom: 1px solid; border-image: linear-gradient(...) 1;.

🟡 LOW Mobile menu CTA text diverges from desktop CTA after this change — src/components/Header.astro

The diff renamed the desktop CTA from 'Launch Agent' to 'Open console' (line 22), but the mobile-menu link at line 40 still reads >Launch Agent</a>. Same destination (https://ai.tangle.tools), two different labels in one component. Impact: inconsistent product voice between desktop and mobile nav for the primary conversion action. Fix: change line 40 to Open console to match, or drive both from one cons

🟡 LOW Silent fallback hides stale brief-ID references — src/data/art-review.json

art-review.astro:19 uses (briefs[k] || '').trim()... and art-review.astro:44 renders briefs[g] directly, so a JSON entry whose brief field stops matching an export key would render an empty

 and a 0-line meta instead of failing the build. Not a current bug (all 9 IDs resolve), but the data contract between art-review.json and the two .mjs exports is enforced only by convention. Optional: a build-time assert in the .astro frontmatter that every candidate.brief has a non-empty briefs[candidate.brief], so a rename or typo fails loudly.

🟡 LOW VIDEO_MOTION exported but never imported or rendered — src/data/briefs-v2.mjs

grep across src/ finds VIDEO_MOTION defined at briefs-v2.mjs:134 but no importer (art-review.astro:5 imports only BRAND_CORE, HERO_FIELD_V2, CARD_SANDBOX, CARD_INTELLIGENCE, CARD_INFERENCE; art-review.json never references VIDEO_MOTION). It is dead content. Either drop the export or add it to the JSON manifest if it represents a v2 motion round that should be reviewed. Harmless to merge as-is.

🟡 LOW Stale font-stack comment in global.css contradicts the Manrope removal — src/layouts/BaseLayout.astro

BaseLayout.astro:64 drops Manrope from the Google Fonts URL, consistent with --font-display now resolving to 'Inter' (global.css:340). However global.css:333-335 still documents the stack as 'Manrope for display'. That comment lives outside this shot's files so it is not a blocking finding here, but the layout change and the stale comment are coupled — flag for the global.css shot to update the docstring so future readers don't re-add Manrope.

🟡 LOW Table block-mode on mobile drops border-radius clipping nuance — src/layouts/BlogLayout.astro

BlogLayout.astro:418-432 sets display:block + overflow-x:auto on tables at <=640px. The desktop rule relies on overflow:hidden for the 8px border-radius (line 391). In block mode the outer table is the scroll container, so the rounded corners may render correctly but the visual interplay of overflow-x:auto + border-radius is browser-dependent (some engines clip the scrollbar inside the radius, some don't). Minor cosmetic risk only; no functional regression since the original code had the same block+scroll pattern. Worth a mobile screenshot before merge.

🟡 LOW brand-kit uses inline script instead of BaseLayout bodyClass — src/pages/brand-kit.astro

The page injects <script is:inline>document.body.classList.add('brand-kit-page')</script> to style the nav, but BaseLayout already accepts a bodyClass prop. Pass bodyClass='brand-kit-page' to avoid a FOUC and remove the inline script.

🟡 LOW Build verification not performed (no node_modules in worktree) — src/pages/index.astro

Worktree has package.json (astro ^6.0.5, three ^0.185.1 both declared) but no node_modules, so 'astro build' could not be executed before scoring. The TypeScript/Astro frontmatter in [series].astro (getStaticPaths returning {params:{series}, props:{seriesName, posts}}, destructured via Astro.props), the redirect in research.astro (return Astro.redirect('/blog/series/the-self-improving-stack', 301)), and the JSON+MJS imports in art-review.astro all look syntactically and type-correct by inspection, but the global verifier should confirm the build passes before merge. No assertion of build-readiness is made in this shot.

🟡 LOW Orphaned CSS selectors reference class names that never appear in markup — src/pages/index.astro

Lines 645-648 define '.run-head h2, .loop-head h2, .wire-copy h2' and line 2062 references '.harness-grid' in a @media block — grep across the full file finds zero matches for these class names in the markup (only the CSS rules themselves). They are leftovers from an earlier template version. No functional impact (rules just never match), but they are dead code that will confuse the next editor. Fix: delete the '.run-head/.loop-head/.wire-copy h2' block at 645-648 (folding any intended style into the section h2 rule at 629-637) and remove

🟡 LOW index.astro event listeners and observers are never cleaned up — src/pages/index.astro

ResizeObserver instances and window.addEventListener for pointermove/scroll are not disconnected/removed. For the current MPA this is acceptable, but if client-side routing is introduced later these will leak per navigation. Store references and disconnect on pagehide/unload.

🟡 LOW .wf-h3 class bound to h2 type tokens — src/styles/global.css

.wf-h3 (lines 2204-2210 and the !important override at 3352-3354) now uses var(--type-h2-weight)/--type-h2-track/--type-h2-line. Visually intentional (this Webflow class sits between h2 and h3), but coupling a class named 'h3' to h2 tokens will mislead future maintainers reading the type scale. Minor naming/coupling nit; no runtime impact since all vars resolve.

🟡 LOW Divergent .wf-h1 line-height values — src/styles/global.css

Line 2197 sets line-height: 0.95;, but the later override at line 3323 uses line-height: var(--type-h1-line) !important; (1.02). Because the later rule is both later in the cascade and marked !important, the 0.95 value is dead code and contradicts the PR's stated goal of a single source of truth for type scale.

🟡 LOW Divergent .wf-h3 line-height values — src/styles/global.css

Line 2208 sets line-height: 1;, while the later override at line 3354 uses line-height: var(--type-h2-line) !important; (1.06). The earlier value is overridden everywhere, leaving a stale declaration that should align with the token.

🟡 LOW New --depth-0/--depth-5 tokens are dead code; ladder middle rungs still undefined — src/styles/global.css

grep for var(--depth-0) and var(--depth-5) across src/ returns zero consumers; the comment at line 232-239 advertises a 'Full contrast-checked ladder' but only 2 of 6 rungs are shipped. Meanwhile --depth-1/-2/-3/-4 are referenced 30+ times (lines 352, 360, 2340, 2428, 2849, 3074, 3285, 3481, etc.) and are NOT defined in this file or any tailwind/@theme config — they resolve to the unset/initial value. That undefined-rung issue is PRE-EXISTING in base commit 823784a (which also had zero --depth-* definitions), so not a regression, but addin

🟡 LOW Stale comment still claims Manrope is the display font — src/styles/global.css

The @theme comment block at lines 333-335 reads 'Inter for body, Manrope for display, JetBrains Mono for code' and describes matching the sandbox web app, but --font-display was changed to "Inter", sans-serif (line 340) and Manrope is no longer loaded anywhere (no @font-face in src/styles/fonts.css, no Google Fonts link in BaseLayout.astro — confirmed via grep). The newer comment at 337-339 supersedes it, leaving two contradictory statements in the same block. Fix: update or remove the stale sentence at [line 334-335](https://github.com/ta


tangletools · 2026-07-02T19:45:46Z · trace

@tangletools tangletools 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.

❌ 1 Blocking Finding — ada4d040

Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-02T19:45:46Z · immutable trace

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — 91768586

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-03T04:10:27Z

Replaces the pay-as-you-go step-theater with the real Free/Pro/Enterprise
tiers ($0/$29/$99, features from products/sandbox pricing source), linking
to id.tangle.tools/app/plans, indigo-reskinned with Pro highlighted.
Removes the counting proof strip (477+/61+/8/OSS) and the open-source
section per founder direction.

@tangletools tangletools 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.

🟡 Value Audit — sound-with-nits

Verdict sound-with-nits
Concerns 3 (3 weak-concern)
Heuristic 0.0s
Duplication 0.1s
Interrogation 217.4s (2 bridge agents)
Total 217.5s

💰 Value — sound-with-nits

Rebuilds the homepage as a cohesive indigo scroll story (WebGL knot hero + proof strip + split-scroll intelligence chapter + product/pricing/OSS sections) plus a footer, public brand-kit, blog-series route, and internal review surfaces — coherent and on-goal, shipped as one 2086-line monolith that r

  • What it does: Replaces the 188-line master homepage (src/pages/index.astro: a muted
  • Goals it achieves: From the code: (1) give the homepage a single coherent visual identity (one indigo luminance ramp, neutrals, semantic red/green only) replacing the prior dispersion palette and stock video hero; (2) tell a product story in scroll order — proof → harness → trace intelligence → products → price — instead of the old flat feature list; (3) retire weak surfaces (the empty /research index 'burns the wor
  • Assessment: Good change on its merits. The narrative outline is sound and the per-section copy is concrete (real router numbers, verified SDK snippet at index.astro:59-67, corrected harness marks at 95-104). The three.js hero is self-contained with correct fallbacks (poster image, reduced-motion guard, viewport checks at index.astro:417-421). Retiring /research to a redirect rather than leaving a hollow index
  • Better / existing approach: Searched for an existing equivalent before answering. Found src/components/ui/ (Section.astro, Hero.astro, StoryScroller.astro, StatsStrip.astro, FeatureCard.astro, BentoCell.astro, Card.astro, Button.astro, CtaBlock.astro — 12 primitives, landed in PR #5) — but git grep -n 'components/ui/' across all of src/ returns ZERO importers: the entire design system is dead code, imported nowhere. So 're
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 2
  • Bridge warning: opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content

🎯 Usefulness — sound-with-nits

A substantial, self-consistent indigo homepage rebuild that wires correctly into the existing layout/header/footer, ships its only new dep (three) in the lockfile, and links only to pages that actually exist — no dead surface.

  • Integration: Fully reachable. src/pages/index.astro is the root route (maximally used). BaseLayout (src/layouts/BaseLayout.astro:104-108) wraps it via Header+Footer, both updated in this PR. The new Footer's /brand-kit link resolves to a real 527-line page (src/pages/brand-kit.astro). Header.astro drops the /research link and /research.astro now 301-redirects to a real blog series — consistent. three@0.185.1 i
  • Fit with existing patterns: Matches the established pattern. Every page is a self-contained .astro that imports BaseLayout (src/pages/blog/index.astro, research.astro, etc. all do this); index.astro follows the same grain, just larger. It extends the global.css token system rather than forking it — adds an indigo ladder (--depth-0, --paper-0, --ink, etc. at src/styles/global.css:232-251) and retunes --type-* and --font-displ
  • Real-world viability: Happy path and the two main fallbacks are solid: prefers-reduced-motion hides the canvas and shows the poster (index.astro:795-798), and narrow viewports (<861px) skip WebGL and the scroll choreography in favor of static stacks (index.astro:423, 532, 573, 607). One gap vs. the PR's own claim: the 'no-JS' fallback is only partial — #hero-knot defaults to opacity:0 and .hero-poster to display:none (
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🎯 Usefulness Audit

🟡 No-JS / WebGL-fail hero fallback is weaker than the PR claims [robustness] ``

PR body states 'static fallbacks for reduced-motion, no-JS, and narrow viewports throughout.' Reduced-motion and narrow-viewport fallbacks work (index.astro:795, 423). The no-JS path does not fully: .hero-poster is display:none by default (index.astro:776) and is only flipped on by JS-added html.static-hero (index.509) or the reduced-motion media query — so a no-JS visitor without that preference sees a blank hero-art area. Same edge affects a WebGL context failure, since new THREE.WebGLRenderer

💰 Value Audit

🟡 Homepage is a 2086-line monolith with a 1471-line inline <style> block [maintenance] ``

src/pages/index.astro is now 2086 lines (style block alone spans 615-2086); the next-largest page is brand-kit.astro at 527 and the median page is <250. The old master index.astro was 188 lines. The choreography + three.js script (411-613) and every section's CSS live in one file, so any visual tweak to a single section means editing a 1.5k-line stylesheet. The codebase already has a component layer (src/components/ui/) whose Section.astro docstring explicitly says it exists to 'replace the wf-s

🟡 New sections re-derive ui/ primitives (StoryScroller / StatsStrip / FeatureCard) that already exist but are dead [duplication] ``

src/components/ui/ contains StoryScroller.astro, StatsStrip.astro, FeatureCard.astro, BentoCell.astro (git ls-tree origin/master confirms all 12 are tracked). The new homepage's .intel-flow split-scroll (index.astro:202-306) maps to StoryScroller, .proof-strip (142-151) maps to StatsStrip, and .products-section (307-330) maps to FeatureCard. git grep -n 'components/ui/' -- 'src/**' returns zero matches, so these primitives are currently imported by nothing — this PR neither uses nor revives th


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260703T043357Z

@tangletools

Copy link
Copy Markdown
Contributor

❌ Needs Work — 91768586

Review health 100/100 · Reviewer score 0/100 · Confidence 95/100 · 32 findings (2 high, 5 medium, 25 low)

glm deepseek aggregate
Readiness 18 0 0
Confidence 95 95 95
Correctness 18 0 0
Security 18 0 0
Testing 18 0 0
Architecture 18 0 0

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision.

Blocking

🔴 HIGH 24 orphan assets: zero source-code references to any file in this shot — public/images/art-review/01-hero-v2-01.webp

All 24 files in this shot are new public/ additions with no consumer code. Searched .tsx,.ts,.jsx,.js,.mdx,.md,.json,.css,*.scss for every asset path and naming variant (art-review, card-sandbox, sandbox-full, sandbox-workspace, opencode.svg, claude-code.svg, pi.svg) — zero matches. The art-review.json data file that catalogs them is also unreferenced by any code. This means 1.7MB of dead assets committed without any render path. Either add the consuming page/component in this PR or split the assets into a follow-up PR with their consumer.

🔴 HIGH Homepage CSS duplicated and with conflicting values across two stale copies — src/pages/index.astro

Lines 829-1178 define .products-section, .statement, .statement-grad, .product-cards, .p-card (+ :hover, -art, -cyan/-magenta/-indigo, -body, -link), .sb-section, .sb-flow, .sb-sticky, .sb-head, .sb-rail, .sb-tile, .sb-logo, .sb-name, .sb-cmd, .sb-run, .sb-lines, .sb-line, .sb-node, .sb-node-label, .intel-section, .intel-flow, .intel-sticky, .intel-step, .istage, .icopy/.ipane, .wf-card, .ann-card, .ship-fan/.ship-card, and .intel-flow.static. Lines 1359-1743 define the SAME selectors a SECOND time verbatim. Three selectors carry C

Other

🟠 MEDIUM Anthropic brand mark in claude-code.svg — trademark risk — public/images/harness/claude-code.svg

The SVG path at line 3 reproduces Anthropic's Claude logo mark (the stylized 'C'). Anthropic's brand guidelines restrict use of their logo in third-party products without permission. Even if this is intended for a tools/integrations section, using the actual trademarked mark rather than a generic code/terminal icon is a legal exposure. Replace with a generic harness/tool icon or obtain explicit brand permission.

🟠 MEDIUM Mobile menu CTA text inconsistent with desktop nav — src/components/Header.astro

The desktop nav CTA was renamed from 'Launch Agent' to 'Open console' (line 22), but the mobile menu CTA (line 40) was not updated and still reads 'Launch Agent'. This creates an inconsistent user experience between desktop and mobile. The mobile menu CTA at line 40 is hardcoded separately from the desktop CTA ([line 21-24](https://github.com/tangle-network/tangle-website/blob/91768586d78cd58dd71c7190443d02

🟠 MEDIUM index.astro: ~200 lines of dead CSS for nonexistent HTML elements — src/pages/index.astro

Lines 1611-1837 define styles for .intel-head, .stage-nav, .stage-area, .stage, .stage-0/1/2, .sim-card, .sim-card-dim, .sim-head, .trace-list, .t-logo, .sim-note, .analyst-card, .analyst-label — none of these classes appear in the HTML markup. The page uses the istage/ship-card pattern instead. This CSS is leftover from a prior design iteration and increases page weight with zero effect. It also redefines .intel-section, .intel-flow, .intel-sticky, .intel-step with different values (340vh vs 430vh height, column vs row flex direction), overriding the active definitions at [lines 1081-1101](https://github.com/tangle-networ

🟠 MEDIUM index.astro: ~250 lines of literal CSS duplication — src/pages/index.astro

Lines 1359-1609 are an exact copy of lines 829-1080 (entire .products-section, .statement, .product-cards, .p-card, .sb-section, .sb-flow, .sb-sticky, .sb-head, .sb-rail, .sb-tile, .sb-lines, .sb-line, .sb-node, .sb-node-label blocks). CSS cascade makes the second copy override the first identically, so no visual bug, but it adds unnecessary page weight. Likely a merge/rebase artifact from iterating on the layout. Remove the duplicate block ([lines 1359-1609](https://github.com/tangle-network/tangle-webs

🟠 MEDIUM ~215 lines of CSS reference HTML classes that do not exist in the template — src/pages/index.astro

Lines ~1611-1826 define .intel-head/.intel-head h2/.intel-head p, .stage-nav/.stage-nav li/.stage-nav li::after and their [data-stage] rules, .stage-area, .stage/.intel-flow[data-stage] .stage-0/1/2, .sim-card/.sim-card-dim/.sim-head, .trace-list/.trace-list li/.t-logo/.trace-list em.hot/.sim-note, and .analyst-card/.analyst-label. NONE of these class names appear anywhere in the body markup of this file — the template uses .intel-h2, .istage/.istage-0/1/2, .istage-area, .icopy, .ipane, .wf-card/.wf-analyzed, .ann-card, .ship-fan/.ship-card instead. This is leftover CSS from a previous design iteration (stage-nav + sim-card + analyst-card) that was replaced by the istage+wf-card+ann-card design but never removed. Impact: pure dead weight, no rendering effect, but it misleads a

🟡 LOW three.js statically imported on homepage script (bundle-size implication, not manifest defect) — package.json

Adding three (~600KB minified, ~150KB gzipped) is fine at the manifest level, but src/pages/index.astro:412 imports THREE + 3 postprocessing addons in a top-level <script> (not a dynamic import), so the entire three bundle ships on the homepage. Worth confirming via pnpm build that the homepage chunk size is acceptable; if not, the consuming files should switch to dynamic import + IntersectionObserver. No action required on package.json itself.

🟡 LOW opencode.svg: nested SVG + dead CSS + no accessibility — public/images/harness/opencode.svg

The file wraps an inside another with identical 512x512 dimensions — the inner element creates a redundant viewport. The <style> block (lines 5-7) sets filter: none for both light and dark color-scheme queries, producing zero effect. Missing <title> and role='img' for screen readers. If this asset becomes used, fix the structure to a single SVG root and remove the dead style block.

🟡 LOW Orphaned product images — not referenced anywhere in source — public/images/product/sandbox-full.webp

sandbox-full.webp (2556x1758, 137KB) and sandbox-workspace.webp (1404x934, 62KB) have zero references across the entire repo: git grep -i sandbox-full|sandbox-workspace over all .astro/.ts/.mjs/.json/.mdx returns nothing. These are dead assets that inflate the deployed site bundle by ~200KB with no rendering benefit. Impact: wasted bandwidth/repo size; no functional break. Fix: either wire them into the sandbox product section (likely intended given the card-sandbox theme) or remove them from this PR.

🟡 LOW Unreferenced product screenshots — potential PII/token risk — public/images/product/sandbox-full.webp

sandbox-full.webp (2556x1758, 137KB) and sandbox-workspace.webp (1404x934, 62KB) are full-resolution product screenshots not referenced in any source code. Cannot verify binary WebP content for embedded PII, internal URLs, API keys, or staging environment details. Before they become referenced, visually inspect both screenshots to confirm no sensitive data is exposed in a public website asset.

🟡 LOW Comment claims three distinct product hues but all three are identical — src/components/Footer.astro

The frontmatter comment (lines 2-3) says 'The hairline is the one place on the page where all three product hues sit on one continuous line,' implying three distinct hues. But the products array (lines 5-7) sets hue: '#818CF8' identically for Sandbox, Intelligence, and Inference, and the .ft-domain spans all render the same color. The comment is misleading vs. the implementation. Either update the comment or differentiate the hues if distinct product coloring was intended.

🟡 LOW Decorative SVGs in social links lack aria-hidden — src/components/Footer.astro

The X and GitHub SVG icons (lines 69, 72) inside social links that already have aria-label attributes do not carry aria-hidden='true'. While modern screen readers typically treat title-less SVGs as decorative, explicitly marking them aria-hidden='true' is the robust pattern and prevents any risk of the SVG path data being read aloud. Fix: add aria-hidden='true' to both elements.

🟡 LOW Hardcoded copyright year in Footer — src/components/Footer.astro

Copyright year is hardcoded as '© 2026 Tangle' (line 66). This requires a manual code change each year. The previous footer had the same pattern ('© 2026 Tangle. All rights reserved.'), so this is not a regression. Consider using new Date().getFullYear() in the frontmatter to auto-update.

🟡 LOW Rewrite orphans ~120 lines of .wf-footer-* CSS in global.css — src/components/Footer.astro

The old footer used .wf-footer-* classes styled in src/styles/global.css (lines 1485-1620 plus responsive overrides at 1665, 1756, 1894-1903). The new markup uses scoped .ft-* classes inside a <style> block, so every .wf-footer-* rule is now dead CSS. global.css is outside this shot's file list so it is not formally reportable as a fix-here, but it is a direct consequence of this component rewrite and should be swept in the same PR to avoid shipping ~120 lines of dead selectors. Confirmed via grep: no remaining consumer of any .wf-footer-* class in src/.

🟡 LOW Mobile menu CTA label diverges from desktop after rename — src/components/Header.astro

This shot renamed the desktop CTA from 'Launch Agent' to 'Open console' (line 22) but the mobile menu link at line 40 still reads 'Launch Agent'. grep confirms 'Launch Agent' now appears exactly once in the codebase, only here. Same destination (https://ai.tangle.tools), two different labels in the same component on different viewports. Fix: change line 40 text to 'Open console' to match.

🟡 LOW art-review.json references images without directory prefix — src/data/art-review.json

Each entry stores only a bare filename (e.g. '01-hero-v2-01.webp') with no directory prefix. The actual files live at public/images/art-review/. Any consumer will need to hardcode or prepend the path, creating a fragile coupling between the data file and its assets. Include the full public/images/art-review/ prefix in the JSON, or use a relative path from the JSON's own location.

🟡 LOW Unused VIDEO_MOTION export — src/data/briefs-v2.mjs

VIDEO_MOTION is exported but never referenced in art-review.json and never imported by art-review.astro. Harmless for a briefs library (extra brief kept on file), but if it is not intended to ship, remove it to keep the data file tight. No functional impact.

🟡 LOW No tests or link checks cover the new routes — src/pages/blog/series/[series].astro

package.json exposes check:links (linkinator), check:blog, check:copy, check:models — none of which exercise /blog/series/*, /brand-kit, /knot-lab, /ideas-review, /art-review, or the rewritten / (homepage). The [series].astro getStaticPaths generates one route per series; if a future blog post sets a series with characters that slugify ambiguously (e.g. a series name containing '&' which becomes 'and', or a name that collapses to empty after slugification), the route would silently drop or collide with no test catching it. No assertion-level test exists. Impact: low for marketing pages, but the series slugify path is the kind of mapping that regresses without a guard. Fix: add a check:links run against the built site as a CI gate (linkinator already in devDeps) and a unit test on slugifySe

🟡 LOW blog/series/[series].astro: slugify regex inconsistency with blog/index.astro — src/pages/blog/series/[series].astro

blog/index.astro uses replace(/^-|-$/g, '') to trim trailing dashes (removes at most one leading/trailing dash). blog/series/[series].astro uses replace(/^-+|-+$/g, '') (removes all leading/trailing dashes). Both produce identical slugs for all current series names, but a series name producing multi-dash edges (e.g. '---My Series---') would cause a URL mismatch between the link href and the route param. Align both slug functions to use the same regex.

🟡 LOW index.astro: .harness-grid responsive rule references nonexistent class — src/pages/index.astro

The responsive rule .harness-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } targets a class that does not appear anywhere in the HTML template. The harness tiles are rendered in .sb-rail grid, not .harness-grid. This rule is dead code. Remove it.

🟡 LOW knot-lab.astro eagerly imports three.js + 3 postprocessing passes with no lazy boundary — src/pages/knot-lab.astro

The page does import * as THREE from 'three'; import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass }...; import { UnrealBloomPass }... at the top of a single <script> block with no dynamic import. For an internal, unlinked, noindex lab page this is acceptable, but it means the full three+postprocessing graph is compiled into a chunk keyed to this route. Confirmed three@^0.185.1 is in package.json and the addons subpath resolves, so this builds fine; flagging only because the same eager pattern appears in index.astro's hero-knot script where it IS user-facing. No fix required for knot-lab specifically.

🟡 LOW /research redirect page is not excluded from the sitemap — src/pages/research.astro

astro.config.mjs sitemap filter only excludes '/preview/'. /research still exists as a generated page (it outputs a redirect Response), so it will be listed in sitemap.xml pointing search engines at a URL whose only behavior is to bounce elsewhere. Minor SEO cleanliness: add !page.endsWith('/research') to the sitemap filter, or add export const prerender = false semantics that drop it from the static output. Not a functional bug.

🟡 LOW research.astro uses a 301 permanent redirect for a page described as 'dormant' — src/pages/research.astro

The comment says 'Research is dormant until >=2 real studies exist' and that the route 'returns as a dense dated publications index when real papers land.' That framing implies a temporary suspension, but Astro.redirect('/blog/series/the-self-improving-stack', 301) emits a 301 Moved Permanently. Search engines (Google especially) cache 301s for a long time and will keep routing /research to the blog series even after a real /research index is published, resisting re-indexing. If the intent is genuinely to revive /research, a 302 (or 307) communicates 'temporarily elsewhere' and keeps the URL crawlable for its future content. Fix: use 302 unless the team has decided /research is permanently retired. Note the site is output:'static' with the Cloudflare adapter, so the redirect is materiali

🟡 LOW --depth-0 and --depth-5 defined but unused; ladder comment overpromises — src/styles/global.css

rg across src/ shows var(--depth-0) and var(--depth-5) have zero consumers, while var(--depth-1..4) are used 21x in this file plus BentoCell, CodeWindow, StoryScroller, ExamplePanel, Section, FeatureCard, StatsStrip, Hero — and none of depth-1/2/3/4 are defined anywhere (pre-existing, falls back to invalid/transparent). The new comment claims a 'full contrast-checked ladder' but only the two endpoints ship, so the actively-consumed middle rungs stay broken and the new endpoints are dead code. Impact: misleading tokens, no functional gain. Fix: either define depth-1..4 to match the existing usages, or remove depth-0/depth-5 until consumers exist.

🟡 LOW --editorial-muted retains legacy value diverging from new ink ladder — src/styles/global.css

The new color system defines --ink-muted: #5E5E7A (line 248) but --editorial-muted keeps its original #4a4c5f (line 228) instead of referencing var(--ink-muted). The editorial variables (--editorial-paper, --editorial-ink, --editorial-nav-bg) were updated to use the new ladder, but --editorial-muted was skipped. Blog/series pages consuming --editorial-muted (src/pages/blog/index.astro:218, src/pages/blog/series/[slug].astro:137) will render a notably darker muted color than other pages using --ink-muted.

🟡 LOW --font-display fallback chain shortened — src/styles/global.css

--font-display went from '"Manrope", "Inter", sans-serif' to '"Inter", sans-serif'. If Inter's webfont fails to load (network/CSP), text now falls straight to the browser default sans-serif instead of a second named family. Minor regression in font resilience; acceptable if intentional but worth a deliberate call.

🟡 LOW --type-h4-* tokens defined but unconsumed by .wf-h4 — src/styles/global.css

--type-h4-weight: 600, --type-h4-track: -0.012em, --type-h4-line: 1.25 are defined (lines 285-288) but .wf-h4 (line 2219, sandbox-ui) uses hardcoded font-weight: 700 and letter-spacing: -0.015em, and .wf-h4 in the foundation reset (line 3365) uses font-weight: 700 !important and letter-spacing: -0.01em !important. The type-h4 tokens are dead code for their namesake class. Either the .wf-h4 rules should consume thes

🟡 LOW Duplicate --editorial-* declarations leave first set dead — src/styles/global.css

Inside the same :root block, --editorial-paper/--editorial-ink/--editorial-nav-bg are declared at lines 226-230 (#f4f4f9 / #191c24 / rgba(12,11,29,0.88)) then re-declared at lines 250-252 (var(--paper-0) / var(--ink) / rgba(17,18,45,0.88)). The later declarations win in the custom-property cascade, making lines 226-230 dead. The two values differ (paper #f4f4f9→#F5F5FB; nav-bg rgb(12,11,29)→rgb(17,18,45)),

🟡 LOW Duplicate --editorial-paper/--editorial-ink definitions in global.css — src/styles/global.css

Outside this shot's files but consumed by it: --editorial-paper and --editorial-ink are defined twice in :root (global.css:226-227 then redefined at 250-251). The second pair wins, shifting paper from #f4f4f9 to var(--paper-0)=#F5F5FB — a deliberate brand refresh per the surrounding comment, not a bug, but the first definition is now dead and misleading. Not blocking; flag for a follow-up cleanup pass.

🟡 LOW Stale hardcoded line-height in sandbox-ui .wf-h1 block — src/styles/global.css

Sandbox-ui .wf-h1 uses a hardcoded line-height: 0.95 that contradicts --type-h1-line: 1.02. The foundation reset at line 3323 wins via !important (so visual output is correct at 1.02), but the sandbox-ui rule is misleading — anyone reading it would think the authoritative line-height is 0.95. Should use var(--type-h1-line) for consistency with the foundation reset.

🟡 LOW Unconsumed CSS custom properties: --depth-0 and --depth-5 — src/styles/global.css

Defined as --depth-0: #080814 (line 240) and --depth-5: #2E2C56 (line 241). Neither variable is consumed anywhere in the codebase. The comment says 'wells, page edges, behind hero art only' but grep confirms zero consumers. Either consumers are missing (the hero should reference these) or this is dead code. If these are for future use, document that explicitly; otherwise they're misleading.


tangletools · 2026-07-03T05:22:14Z · trace

@tangletools tangletools 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.

❌ 2 Blocking Findings — 91768586

Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 84 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-03T05:22:14Z · immutable trace

drewstone added 8 commits July 2, 2026 23:49
… lanes

Contest prototype at /lab/woven (noindex). 15 light fibers enter as a
chaotic woven tangle (3 red = wasted reruns) and resolve left-to-right
into clean parallel lanes as the waste is routed out. Three.js fresnel
fiber shader + bloom + grain, pointer-scrubbable, reduced-motion shows
the resolved state. Replaces the decorative torus knot with an object
that depicts the mission: tangle becoming order.
…forcegraph, flowfield, braid, wordmark)

Contest prototypes at /lab/<slug>, each a self-contained noindex page:
a distinct high-fidelity take on tangle resolving into order. Built by
Codex GPT-5.5 (xhigh) via the companion runtime. Gallery cards flipped live.
woven v2: vertical-scroll-driven resolution (fixes the pointer snap-back),
ACES filmic tonemapping (no blown highlights), traveling light-pulses inside
the fibers (model calls flowing), 16 legible woven strands, deepened red
waste (no magenta bleed), fog + dolly depth, vignette. Removes the rejected
rope and forcegraph prototypes.
…material test

unknot: pink/magenta removed, clean indigo trefoil, vertical-scroll unknots it.
braid: ACES + low bloom fix the overexposure; reads as a real fiber.
flowfield: rebuilt for presence (currently over-bright, needs trail fade fix).
woven-glass: MeshPhysicalMaterial transmission + PMREM indigo env — confirms
glass alone is too dim to carry a hero; fiber-optic needs an emissive core.
New /lab/studio: the run rendered as a twisting metallic cable with live
controls, material (glow shader / metallic env-mapped / glass physical) and
world (void / procedural nebula / procedural aurora / fal image slot), scroll
or slider to resolve. Finding: metallic + the structured cable knot is the
hero direction (substantial, complex, premium); additive glow suits looser or
resolved geometry. Retires the toy-trefoil tangle for a real woven helix.
Background quad now renders in the main scene (renderOrder -1, depthTest off)
so nebula/aurora/image worlds actually show through instead of being
overwritten by the composer. Enables the metallic run in a cosmic vista.
…mick

New /lab/physical: the run rendered as a real cast-glass / matte-ceramic /
brushed-metal object under RoomEnvironment studio lighting on a soft indigo
backdrop. No additive glow, no bloom, no void, no space. Proves the
Igloo/Lusion physical-craft register reads as sophisticated where neon-on-black
reads juvenile. Grounded in the 31-site teardown finding that great motifs are
physical and product-derived, not decorative glow.
drewstone added 5 commits July 3, 2026 17:52
New /lab/agent: Fauna Robotics' spec-explorer pattern in Tangle's taxonomy.
Left panel is the agent portrait (a tangle whose strand density = config);
right is a tabbed Harness/Model/Tools/Sandbox selector with live readouts
(tools, sandbox, traces, win-rate) and a profile.json export. Deploy resolves
the tangled portrait into ordered lanes and flips draft->deployed: configuring
an agent IS the untangling act. From the 7-site awwwards reverse-engineering
playbook (Lumena/Units/Fauna the flagged references).
…t self-improve

New /lab/agents: agents as a collectible character wall. Each agent is a
fal-rendered robot with generative traits (harness/model/tools = its config),
a tier (Common..Epic) and a level. The featured agent can be deployed: it runs,
streams traces, then earns a visible power-up (+retry-scoping, +parallel-tools,
etc), levels up, and its win-rate climbs. Self-improving agents made literal and
fun. 9 fal agent-character candidates in 3 styles (matte designer / crystal /
woven-fiber); indigo-locked roster expansion generating.
…tiers, equipped upgrades

Fixes 'rare ones all look the same': tier is now derived from the character
family (woven/ornate=Epic, crystal=Rare, matte=Common/Uncommon), never a random
sticker; every card is a distinct render; wall sorts by rarity. Epic/Rare tiers
get real treatment (aura, holo shimmer, glow frame). Featured agent earns visible
equipped-upgrade pills (+retry/+parallel/+cache) that attach on each run, with the
aura intensifying and level/win-rate climbing. 30 fal characters total (matte/
crystal/woven + purpose-built common/rare/epic renders), indigo-graded via CSS.
scripts/agent-forge.mjs is a two-stage, batched, deterministic workflow:
- Stage 1 (rare/expensive): base robot BODIES generated with FLUX (fal), reused.
- Stage 2 (this script, free + reproducible): composite the REAL harness logo
  (chest emblem), model, tools, tier, and level onto a base body from AgentProfile
  data via headless Playwright render. Real SVG/PNG logos are pixel-accurate and
  free, so features are reproducible from config (not hallucinated by an image model).

Assets inlined as base64 data URIs -> self-contained, no server dependency.
Same --seed reproduces the exact roster. Cheap to scale to 1000s: bodies are
reused, compositing is free. Usage: node scripts/agent-forge.mjs --count 10 --seed 3
Featured agent now EVOLVES through stages (base -> armored core -> winged/haloed)
via FLUX Kontext image edits chained on the same character, driven by the deploy
->trace->improve loop. Upgrades transform the robot itself, not labels. Adds
scratchpad evolve-fal.mjs (Kontext edit pipeline) output + harness logo in the
featured trait chip.

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — 5220fa91

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-09T04:13:50Z

@drewstone

Copy link
Copy Markdown
Contributor Author

Session wrap-up (2026-07-03)

Pushed 15 commits of hero/brand exploration. Not for merge yet — this branch now holds both the v18 homepage rebuild and an internal /lab exploration suite (all noindex, unlinked).

What's in /lab (exploration, decide before merge)

  • /lab/agentsthe leading direction: agents as collectible characters that visibly self-improve (deploy → trace → evolve, the picture upgrades base → armored → winged/haloed via image-edit)
  • /lab/agent — AgentProfile configurator (Fauna-style)
  • /lab/physical, /lab/studio — physical-material hero tests (the sophisticated register; retires the neon-glow direction)
  • scripts/agent-forge.mjs — reproducible AgentProfile → portrait pipeline
  • earlier tangle→order prototypes (woven/unknot/braid/etc.)

⚠️ Pre-merge requirements

  1. 179MB of generated agent art is committed under public/images/agents/. This should NOT ship in the marketing repo — move to on-demand generation or external storage, and gitignore, before any merge.
  2. copy CI is failing — pre-existing: the LLM copy-auditor times out (20s) on old blog pages; unrelated to homepage/lab. Separate fix.
  3. Decide whether /lab ships (behind noindex) or gets stripped for the real homepage PR.

Nothing here is deployed; main auto-deploys only on merge.

@tangletools tangletools 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.

🟠 Value Audit — better-approach-exists

Verdict better-approach-exists
Concerns 7 (4 medium-concern, 2 low, 1 weak-concern)
Heuristic 0.1s
Duplication 0.0s
Interrogation 492.5s (2 bridge agents)
Total 492.6s

💰 Value — better-approach-exists

Replaces the one-section homepage with an indigo scroll-driven product story, but ships it as a 1,880-line monolith that duplicates existing UI components and bundles a much broader internal lab/agent collection than the homepage requires.

  • What it does: Rebuilds src/pages/index.astro into a multi-section indigo narrative: a Three.js torus-knot hero with fresnel shader and bloom; a scroll-driven 'sandbox' chapter that lights up eight harness tiles and draws SVG trace lines; a sticky-scroll 'intelligence' chapter with a session-trace waterfall, an analyst finding, and a scroll-scrubbed 'deal-out' of artifact cards; product cards; pricing tiers; a
  • Goals it achieves: Unify the public homepage under a single indigo brand ramp, tell the product loop (sandbox → traces → intelligence → shipped improvements), retire the previous dispersion palette, and provide internal surfaces to review art/motion candidates without filing tickets.
  • Assessment: The new homepage direction is coherent and improves on the old one-section hero: it has a clear value proposition, fallback for reduced-motion/no-JS/narrow viewports, and real product links. However, the execution is monolithic: the page is 1,880 lines with duplicated CSS blocks and unused selectors, and it bypasses the component library the rest of the site is migrating toward. The PR also bundle
  • Better / existing approach: The codebase already has reusable components for these exact patterns: src/components/ui/StoryScroller.astro is a scroll-driven sticky narrative with stages/panels, src/components/ui/Hero.astro is the canonical hero block, and src/components/ui/Button.astro, Section.astro, and FeatureCard.astro provide the buttons, sections, and cards. The intelligence stage and CTAs should be built from
  • Model: opencode/kimi-for-coding/k2p7
  • Bridge attempts: 1

🎯 Usefulness — sound

The homepage rebuild is a clean, well-integrated replacement of the old video-hero with a WebGL knot (three.js) plus graceful poster fallbacks; it is the site's primary surface and will absolutely be used.

  • Integration: Homepage lives at / (src/pages/index.astro) wrapped in the existing BaseLayout — the site's primary entry point, fully reachable. three.js (^0.185.1, package.json:40) is imported inside a client-side <script> block (index.astro:376-379); verified the three package exports map ('./addons/' -> './examples/jsm/') resolves EffectComposer/RenderPass/UnrealBloomPass against the installed version, so t
  • Fit with existing patterns: Follows the codebase's established patterns: same BaseLayout, same @tangle-network/brand CSS tokens (global.css:8-10), same component grain. The old video-hero (
  • Real-world viability: Degrades gracefully: prefers-reduced-motion hides the canvas and shows the poster (index.astro:759-762); narrow viewports (<861px) fall back via the static-hero branch (index.astro:382,473); the canvas starts at opacity:0 and only fades in once .ready is set (index.astro:733,471), so a failed/slow WebGL init never shows a broken canvas. Hero copy is server-rendered HTML visible before JS loads. Th
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🔎 Heuristic Signals

🟡 Cruft: console debug added scripts/agent-forge.mjs

  • console.log(forged ${p.id} ${p.tier.padEnd(9)} ${p.harness.name} / ${p.model});

🟡 Cruft: magic number added scripts/agent-forge.mjs

  • const r = mulberry32(SEED * 1000 + i * 97);

💰 Value Audit

🟠 Homepage reimplements the site's scroll-story component instead of reusing it [better-architecture] ``

src/components/ui/StoryScroller.astro (lines 1-287) already implements a sticky-scroll, stage-driven narrative with reduced-motion and mobile fallbacks. The new index.astro adds a second, bespoke scroll controller (src/pages/index.astro:375-577) for the same intelligence-stage pattern. The page should use or extend StoryScroller rather than maintain a parallel controller.

🟠 Homepage ignores the existing UI component library [against-grain] ``

The repo has Hero.astro, Button.astro, Section.astro, and FeatureCard.astro (src/components/ui/). The new homepage inlines its own .btn, .hero, .p-card, .tier, and .close-section styles in src/pages/index.astro:579-1880 instead of composing from these primitives, diverging from the codebase's component-first direction.

🟠 index.astro contains duplicated and dead CSS blocks [maintenance] ``

The stylesheet repeats the products-section block at src/pages/index.astro:793 and src/pages/index.astro:1323, the sandbox block at :905 and :1435, and the intelligence block at :1045 and :1575. It also defines unused selectors such as .code-pane/.pane-head/.add/.del (lines 684-711), .stage-nav/.stage-area/.stage (lines 1628-1707), and .sim-card/.analyst-card/.trace-list/.t-logo (lines 1709-1801) that do not match any markup in the file.

🟠 PR bundles a large internal lab/agent collection with the homepage change [proportion] ``

The title/body describe a homepage rebuild, but the branch also adds 10+ /lab/* prototype pages, src/pages/brand-kit.astro, src/pages/art-review.astro, src/pages/ideas-review.astro, src/pages/knot-lab.astro, scripts/agent-forge.mjs, and many generated agent/brand images. These are not required for the homepage and should be separated so the homepage can be reviewed and shipped on its own.

🟡 Unused constant sandboxSnippet is leftover [maintenance] ``

The sandboxSnippet constant at src/pages/index.astro:61-67 is not rendered anywhere in the new page markup; it appears to be a carry-over from an earlier layout and should be removed.


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260709T044212Z

@tangletools

Copy link
Copy Markdown
Contributor

❌ Needs Work — 5220fa91

Review health 100/100 · Reviewer score 0/100 · Confidence 95/100 · 32 findings (3 high, 8 medium, 21 low)

deepseek kimi-code aggregate
Readiness 0 0 0
Confidence 95 95 95
Correctness 0 0 0
Security 0 0 0
Testing 0 0 0
Architecture 0 0 0

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 8/8 planned shots over 165 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 165 changed files. Global verifier still owns final merge decision.

Blocking

🔴 HIGH Arbitrary file write via profile id path traversal — scripts/agent-forge.mjs

Screenshot output path is join(OUT, ${p.id}.png). A profile id containing ../ can write PNGs outside images/agents/forged/, overwriting files elsewhere. Sanitize id to a safe filename or validate it matches an allowed pattern.

🔴 HIGH Path traversal / arbitrary file read via --profiles input — scripts/agent-forge.mjs

When --profiles is supplied, p.base and p.harness.logo are read with readFileSync(join(PUB, relPath)). An absolute path or ../ traversal in those fields escapes public/ and can read arbitrary files (e.g., /etc/passwd). The code also follows an absolute path if relPath starts with '/'. Validate that resolved paths stay inside PUB or restrict to a known asset whitelist.

🔴 HIGH Duplicate CSS blocks with conflicting cascade values and dead code — src/pages/index.astro

The <style> block contains fully duplicated CSS for three sections: .products-section (lines 793 and 1324), .sb-section (lines 906 and 1436), and .intel-section (lines 1046 and 1576). The first two duplicates are identical (~260 lines of redundant CSS). The intelligence section duplicate is worse: it uses different class names (.stage, .stage-nav, .sim-card, .trace-list, .analyst-card) that do not exist in the HTML (whic

Other

🟠 MEDIUM --profiles JSON is completely unvalidated, can crash with TypeError — scripts/agent-forge.mjs

Line 87: if (PROFILES_PATH) return JSON.parse(readFileSync(PROFILES_PATH, 'utf8')); — the parsed result is used directly in cardHTML(p) (line 168) which accesses p.tier (as a dictionary key for tierGlow), p.base, p.harness.name, p.harness.logo, p.model, and p.tools.map(...) without any existence checks. A profile missing harness crashes with Cannot read properties of undefined. Missing tools crashes with p.tools.map is not a function. Missing tier produces undefined CSS values. No schema validation or defensive

🟠 MEDIUM Chromium browser not closed on error — resource leak — scripts/agent-forge.mjs

Lines 164-176: chromium.launch() and the for-loop iteration (lines 167-174) have no try/finally wrapper. If dataURI throws (missing image, line 32), page.setContent fails, or page.screenshot fails, the exception propagates past browser.close() at line 176, leaking a runnin

🟠 MEDIUM Path traversal in --profiles mode via base/logo/id fields — scripts/agent-forge.mjs

dataURI(relPath) at line 30 calls readFileSync(join(PUB, relPath)). When --profiles is used (line 87, no validation), the base and harness.logo fields from the external JSON flow into dataURI (lines 148, 150). join(PUB, '../../etc/passwd') normalizes to a path outside public/, allowing arbitrary file reads. Similarly, the id field is used as a filename in join(OUT, p.id + '.png') at [line 170](http

🟠 MEDIUM Sort-based shuffle breaks cross-version reproducibility — scripts/agent-forge.mjs

[...TOOLS].sort(() => r() - 0.5) relies on V8's comparator call order, which can change across Node/V8 versions. This contradicts the 'same seed => same set' guarantee. Replace with a Fisher-Yates shuffle using the same mulberry32 PRNG.

🟠 MEDIUM Unescaped profile values injected into rendered HTML — scripts/agent-forge.mjs

p.tier, p.harness.name, p.model, p.id, p.level, and p.tools are concatenated directly into the HTML string. With --profiles this allows HTML/JS injection into the Playwright page and can distort or break the generated card. Escape HTML entities or use a DOM/template library.

🟠 MEDIUM Mobile menu CTA label inconsistent with desktop CTA — src/components/Header.astro

Line 22 changed the desktop CTA text from 'Launch Agent' to 'Open console', but line 40 leaves the mobile-menu CTA as 'Launch Agent'. On narrow viewports users see a different label for the same https://ai.tangle.tools action, which looks like a stale string. Fix: change line 40 to 'Open console' to match the desktop CTA.

🟠 MEDIUM Mobile menu CTA label not updated to match desktop — src/components/Header.astro

Line 22 (desktop CTA): <span class="wf-nav-cta-text">Open console</span>. Line 40 (mobile menu CTA): still reads Launch Agent. The rename missed the mobile menu variant. Mobile users will see the old label "Launch Agent" while desktop users see "Open console" — same link, split label.

🟠 MEDIUM Desktop tables lose horizontal scroll, content may clip — src/layouts/BlogLayout.astro

The old table style used display:block + overflow-x:auto + -webkit-overflow-scrolling:touch, providing scrollable tables at all viewport widths. The new code uses display:table + overflow:hidden + table-layout:auto for viewports >640px. With table-layout:auto, browsers compute column widths from content; if content (unbreakable strings, wide code, images in cells) forces the table wider than its container, overflow:hidden clips the overflow with no way to scroll. The mobile breakpoint (≤640px) correctly restores display:block + overflow-x:auto, but no scroll mechanism exists at 641px+. 23 blog .mdx files contain tables; technical posts with wide content (e.g. pricing-without-hand-waving-wei-token-conversion-markup-dynamic-price-tags.mdx, blueprint-tee-x402-production-gating.mdx) are at ris

🟡 LOW Base body PNGs weigh 36 MB for 10 files — public/images/agents/bases/body-common-01.png

10 new 2048×2048 RGB PNGs total 36 MB (3.3–4.0 MB each): body-common-01 through body-common-05, body-rare-01 through body-rare-05. These are build-time inputs to scripts/agent-forge.mjs (composited via Playwright into output cards), never served to users. While acceptable as build assets, 36 MB in the git tree is heavy. Consider lossless WebP (typically 40–60% smaller) or documenting in the build script that these are intentionally raw PNGs for compositing fidelity.

🟡 LOW Oversized thumbnail asset served full-resolution — public/images/agents/common-01.png

common-01.png is 2048x2048 (~3.4 MB) and is used as a 12rem card thumbnail in src/pages/lab/agents.astro (<img src={/images/agents/${a.img}.png} ... loading="lazy" />). Loading a 3.4 MP image for a small thumbnail inflates bandwidth and LCP. Fix: provide a resized/optimized version (e.g., 512x512 or smaller WebP) and use srcset, or compress the source.

🟡 LOW manifest.json missing trailing newline — public/images/agents/forged/manifest.json

The file ends on the closing ] character without a trailing newline. The build script (agent-forge.mjs) writes via JSON.stringify without appending '\n', causing git to flag 'No newline at end of file'. Minor style fix: append '\n' after the stringify call.

🟡 LOW manifest.json: valid artifact with no code consumer — public/images/agents/forged/manifest.json

manifest.json is structurally valid (10 entries, valid tiers/levels, all referenced paths resolve). However, no Astro page, component, or data loader reads this file — it is write-only. It may be intended as documentation or as input for a future feature, but as of this PR it is orphaned data. If intentional documentation, that's fine; if expected to power a UI, the consumer is missing.

🟡 LOW opencode.svg: nested SVG with dead <style> block — public/images/harness/opencode.svg

The file wraps an inner element inside an outer element, both with identical 512×512 dimensions — the nesting is redundant. Additionally, a <style> block at the end sets :root { filter: none; } for both prefers-color-scheme: light and prefers-color-scheme: dark. filter: none is the CSS default, making these rules dead code. The SVG renders correctly regardless, but the nesting and dead CSS are slop that should be cleaned up.

🟡 LOW Crash when no base images are present — scripts/agent-forge.mjs

If public/images/agents contains no PNGs, discoverBases leaves byTier[t] empty and bases[Math.floor(r()*0)] returns undefined, causing dataURI to throw. Guard against empty pools and provide a helpful error before rendering.

🟡 LOW Empty image pool causes cryptic crash via undefined base index — scripts/agent-forge.mjs

Line 76: if (!byTier[t].length) byTier[t] = pick(/./, '', flat); — if the flat pool flat is also empty (no PNGs in public/images/agents/), all tiers remain empty arrays. Line 101: bases[Math.floor(r() * bases.length)] evaluates to bases[0] = undefined when bases is empty. dataURI(undefined) at line 30 calls readFileSync(join(PUB, undefined)), producing an ENOENT crash with a confusing path. No early

🟡 LOW Epic and Rare tiers share identical body-rare base images from bases/ — scripts/agent-forge.mjs

Lines 71-72: Both Epic (pick(/^body-rare/, 'bases/', bases)) and Rare (pick(/^body-rare/, 'bases/', bases)) use the same regex filter on the bases/ directory. Both tiers pull from the same pool of body-rare-XX.png files, defeating tier-based visual differentiation for bases/-sourced images. The flat-pool fallback for Epic picks epic|woven and Rare picks rare|crystal, so this only affects the bases/ subdirectory, but it means half the tier spectrum shares a body pool.

🟡 LOW Naive argument parser yields silent failures — scripts/agent-forge.mjs

arg() reads args[i+1] unconditionally, so node scripts/agent-forge.mjs --count (no value) parses as NaN and produces zero agents with no error. Add validation for NaN/missing values and emit a clear error message.

🟡 LOW Footer logo image missing loading="lazy" — src/components/Footer.astro

The old footer included loading="lazy" on the Tangle logo <img>. The rewrite dropped that attribute. Since the footer always renders below the fold, lazy loading avoids an unnecessary eager-load on first paint. Trivial perf regression.

🟡 LOW CARD_SANDBOX uses #A5F3FC ('pale ice') outside the canonical brand palette — src/data/briefs-v2.mjs

The BRAND_CORE palette (line 11) defines ice as #67E8F9, and the v3 IDENTITY block (briefs-v3.mjs:20) does the same. CARD_SANDBOX line 72 introduces #A5F3FC as 'pale ice' — a lighter cyan variant not in the canonical set. This could cause image generation models to stray from the defined brand system for this one card.

🟡 LOW Dead export: VIDEO_MOTION never imported — src/data/briefs-v2.mjs

VIDEO_MOTION is exported but has zero consumers anywhere in the repo (grep'd across all file types). It is also absent from art-review.json (no generated images reference it). Either it was intended for a future round, or it should be removed. Dead code in a data module adds confusion about what's in active use.

🟡 LOW blog/series route lacks empty-state for non-matching slugs — src/pages/blog/series/[series].astro

getStaticPaths generates paths for all series with posts, but if an invalid slug is requested directly (e.g. /blog/series/nonexistent), Astro would 404 by default since it's not a generated path. No issue at runtime. However, if in the future getStaticPaths logic changes or posts are deleted, there is no defensive empty-posts rendering in the template.

🟡 LOW Duplicate product-cards CSS block in homepage — src/pages/index.astro

The entire product-cards stylesheet is duplicated: lines 794-903 and 1324-1434 both define .products-section, .statement, .product-cards, .p-card, .p-card-art, .p-card-cyan/.magenta/.indigo, and .p-card-body. The second block overrides the first with identical rules, adding ~110 lines of dead CSS. Remove one copy.

🟡 LOW Hero canvas only initializes at load-time width — src/pages/index.astro

const wide = window.matchMedia('(min-width: 861px)').matches is evaluated once at script load. If a user starts narrow and resizes/rotates to wide, the Three.js hero canvas never initializes and the static poster remains. Evaluate the media query inside the resize handler or gate initialization with CSS so wide viewports get the canvas after resize.

🟡 LOW Unused sandboxSnippet variable and dead code-pane CSS — src/pages/index.astro

The const sandboxSnippet (lines 61-67) is defined but never rendered. Matching CSS rules .code-pane, .pane-head, .code-pane pre, .add, and .del (lines 684-711) have no corresponding markup. Remove the unused variable and styles, or render the snippet in the intended section.

🟡 LOW Unused variable sandboxSnippet defined in frontmatter — src/pages/index.astro

const sandboxSnippet is defined in the frontmatter block but never referenced in the template or script sections. Remove or wire it into the sandbox chapter.

🟡 LOW Unused featured variable — src/pages/lab/agents.astro

const featured = roster.find((a) => a.tier === 'Epic') is assigned but never used; the featured section uses featuredEvo instead. Remove the dead variable.

🟡 LOW Dead CSS: 42 .wf-footer-* rules orphaned after footer rewrite — src/styles/global.css

The old Footer used .wf-footer-* class names. The rewrite replaced every class with a new .ft-* namespace, but global.css retains the full old rule set (~42 rules across lines 1485–1910, 2876–2921, 3216–3217). These rules now match nothing. Also dead: the vault-specific overrides at lines 152–153 (.t-section--vault .wf-footer-tagline and .t-section--vault .wf-footer-legal-link). No rendering impact, but adds ~400 lines of dead CSS to the global stylesheet.

🟡 LOW Duplicate --editorial-* declarations in same :root — src/styles/global.css

--editorial-paper, --editorial-ink, and --editorial-nav-bg are declared at lines 226-230 and then immediately overridden at lines 250-252 within the same :root selector. The first declarations are dead code and can mislead maintainers about the canonical value.

🟡 LOW New --paper-0 diverges from existing vault white #f4f4f9 — src/styles/global.css

--editorial-paper now maps to --paper-0 (#F5F5FB), while .t-section--vault rules and Section.astro still hardcode #f4f4f9 for vault surfaces. This creates a near-imperceptible but real color split between editorial/blog pages and vault sections; either align the hardcoded surfaces to --paper-0 or document the intentional distinction.


tangletools · 2026-07-09T04:52:26Z · trace

@tangletools tangletools 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.

❌ 3 Blocking Findings — 5220fa91

Full multi-shot audit completed 8/8 planned shots over 165 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 165 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-09T04:52:26Z · immutable trace

drewstone added 2 commits July 9, 2026 07:49
30 collection portraits + 2 evolution stages + 1 atmosphere, 1024px webp
(2.1MB total, was 206MB of raw 2048px PNG). Raw output archived in the GTM
assets tree and gitignored; bases/ and forged/ are generated on demand by
scripts/agent-forge.mjs.
@drewstone drewstone force-pushed the feat/homepage-indigo-story branch from 5220fa9 to 993ce13 Compare July 9, 2026 14:51

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — 993ce13f

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-09T14:51:31Z

@tangletools tangletools 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.

🟠 Value Audit — better-approach-exists

Verdict better-approach-exists
Concerns 5 (1 medium-concern, 2 low, 2 weak-concern)
Heuristic 0.1s
Duplication 0.0s
Interrogation 335.5s (2 bridge agents)
Total 335.6s

💰 Value — better-approach-exists

Replaces the 188-line editorial stub with an 1880-line single-file indigo scroll-story homepage (WebGL knot + pinned scroll chapters + deal-out cards + pricing) — it works and is on-goal, but it hand-rolls the exact hero, sticky-scroll, proof-strip and feature-card primitives the repo already ships

  • What it does: src/pages/index.astro goes from 188 lines (editorial-core stub, commit ab44501) to 1880 lines: a fiber-optic torus-knot WebGL hero (three.js fresnel shader + UnrealBloomPass, src/pages/index.astro:376-471) with static/poster/reduced-motion/narrow fallbacks (:381-474); a scroll-driven Sandbox chapter (.sb-flow, :495-519); a scroll-driven Intelligence chapter with a tunable card deal-out (CHOREO/STA
  • Goals it achieves: Unify the site on one indigo luminance ramp (retire the multi-hue dispersion palette), turn the homepage into a single scroll narrative that demonstrates the product (sandbox run → trace waterfall → analyst finding → ship), and promote the knot aesthetic that was prototyped and validated in src/pages/lab/* (woven/unknot/studio) into the production hero. It achieves this coherently: the knot shader
  • Assessment: A coherent, worthwhile change built with real craft (the deal-out is genuinely tunable via CHOREO; reduced-motion/no-JS/narrow fallbacks are present; art is curated, not raw). It matches ONE repo grain — every src/pages/lab/*.astro page also inlines its own three.js + bespoke CSS. But it contradicts the OTHER, documented grain: src/components/ui/ ships a marketing design system (Hero, StoryScrolle
  • Better / existing approach: Looked in src/components/ui/* and src/styles/global.css. The rebuild partially reinvents existing capability: (1) StoryScroller.astro:2-3 is literally 'Sticky-scroll narrative section. Pinned on desktop, stacked on mobile... driven by scroll progress' — exactly what index.astro hand-codes as .intel-flow/.sb-flow with manual getBoundingClientRect scroll math (:500-575); (2) Hero.astro:3-6 exists to
  • Model: opencode/kimi-for-coding/k2p7
  • Bridge attempts: 1

🎯 Usefulness — sound

A coherent, fully-wired rebuild of the homepage as a single-hue indigo scroll story — it is the site's landing page, so it is maximally reachable and will be used; it fits the codebase grain and replaces (does not compete with) the prior homepage.

  • Integration: Reachable by definition: src/pages/index.astro is /. It mounts the existing BaseLayout (index.astro:2,122) and its product/pricing CTAs point at the canonical subdomains already used across src/ (sandbox/router/intelligence/id.tangle.tools — grep counted 4/4/4/3 uses site-wide, matching index.astro). The new three dep (package.json) is genuinely consumed: index.astro:376-439 imports three + Ef
  • Fit with existing patterns: Replaces the existing homepage in place (git diff: index.astro +1856/-? in the same file) rather than adding a competing surface. Uses the established patterns: BaseLayout, global.css token imports, Astro client <script> for browser-only three.js bundling (standard Vite/Astro). The palette change is site-wide and internally consistent — the shader color stops (index.astro:395 `['#C7C9F5','#818CF
  • Real-world viability: Edge paths are covered, not just the happy path. The WebGL knot is guarded by !reduced && wide (index.astro:387, prefers-reduced-motion + min-width:861px); the poster is always in the DOM (index.astro:134) so no-JS and failed-WebGL both degrade to the static image; the knot also pauses rendering when off-screen via IntersectionObserver (index.astro:456) and resizes via ResizeObserver (inde
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🔎 Heuristic Signals

🟡 Cruft: console debug added scripts/agent-forge.mjs

  • console.log(forged ${p.id} ${p.tier.padEnd(9)} ${p.harness.name} / ${p.model});

🟡 Cruft: magic number added scripts/agent-forge.mjs

  • const r = mulberry32(SEED * 1000 + i * 97);

💰 Value Audit

🟠 Homepage hand-rolls the existing shared marketing UI instead of composing it [better-architecture] ``

src/pages/index.astro (~1880 lines, bespoke sb-/intel-/ship-/tier- CSS + inline scroll JS) re-implements primitives that already exist in src/components/ui/: StoryScroller.astro (pinned scroll-progress narrative — its :2-3 doc matches index's .intel-flow/.sb-flow exactly), Hero.astro ('shared hero block... Replaces 8 bespoke hero CSS blocks', :3-6), StatsStrip.astro (proof strip), FeatureCard.astro (product cards). git grep -rln components/ui src/pages src/layouts = empty, so the library is cu

🟡 Hero knot shader is copied verbatim from knot-lab.astro (same PR) [duplication] ``

index.astro:395-419 (uC0-uC3 ramp + '0.22 + 0.62 * fresnel' + pulse) duplicates knot-lab.astro:55-105 line-for-line. knot-lab is an internal noindex review surface added in this same branch, so the duplication is cheap and arguably fine, but if the knot is now the canonical hero, the GLSL/material should live in one shared module both import rather than two copies that will drift.

🟡 Bypasses the global.css token system with ~90 hardcoded hex literals [against-grain] ``

global.css:225-248 defines --brand-dim (#4F46E5), --depth-0..5, --ink, etc. index.astro uses var(--...) only 37 times but hardcodes #4F46E5 20×, #ffffff 18×, #818CF8 9×, plus #C7C9F5/#6366F1/#4338CA — the indigo ramp the PR says it unifies on. Routing these through the existing tokens is what would actually make the single-hue palette enforceable and consistent with the rest of the site.


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260709T145914Z

@tangletools

Copy link
Copy Markdown
Contributor

❌ Needs Work — 993ce13f

Review health 100/100 · Reviewer score 0/100 · Confidence 95/100 · 31 findings (1 critical, 1 high, 12 medium, 17 low)

deepseek kimi-code aggregate
Readiness 0 0 0
Confidence 95 95 95
Correctness 0 0 0
Security 0 0 0
Testing 0 0 0
Architecture 0 0 0

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 8/8 planned shots over 130 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 130 changed files. Global verifier still owns final merge decision.

Blocking

🟣 CRITICAL Base image discovery broken — all 30 assets are .webp, script filters .png only — scripts/agent-forge.mjs

Line 74: readdirSync(join(PUB, 'images/agents')).filter((f) => f.endsWith('.png')) — all 30 files in public/images/agents/ are .webp (common-01.webp through woven-bot-03.webp), zero .png files exist. This causes the flat pool to be empty. The bases/ subdirectory also does not exist (line 77), so bases is empty too. All tier arrays are empty after construction (lines 79-84). The fallback pick(/./, '', flat) ([li

🔴 HIGH Base-body discovery only accepts .png but committed assets are .webp, causing crash — scripts/agent-forge.mjs

readdirSync filters with f.endsWith('.png') at lines 66 and 68, and the fallback at line 76 uses the same flat array. The repo's public/images/agents contains 30 .webp files and 0 .png files (verified via ls and a node snippet). discoverBases() therefore returns empty arrays for every tier, buildProfiles() picks bases[Math.floor(r()*0)] === undefined, and dataURI(undefined) throws ENOENT on public/undefined. Fix: include .webp in the discovery filter and add 'image/webp' to the MIME map, or commit .png base bodies.

Other

🟠 MEDIUM 228KB webp asset will hurt LCP on product cards — public/images/art-review/08-card-sandbox-flux-01.webp

This VP8 webp is 228,504 bytes at 1200×1586. In comparison, similar card images in the same art-review directory are 33–48KB at the same pixel dimensions. The size difference (4.7–6.9×) suggests this file was saved without compression optimization. Estimated ~230ms added on slow 3G. Re-encode with lossless VP8L or a reasonable quality setting (q=75–80 typically cuts 60%+ with no visible loss at display sizes).

🟠 MEDIUM --profiles path is CWD-relative, not project-relative — scripts/agent-forge.mjs

readFileSync(PROFILES_PATH, 'utf8') (line 97) uses the raw CLI argument without resolving it relative to ROOT. Running the script from outside the project root makes --profiles relative/path.json fail silently. Should use resolve(ROOT, PROFILES_PATH) or resolve(PROFILES_PATH) consistently with the rest of the script's path strategy.

🟠 MEDIUM Browser process leaks on render failure — scripts/agent-forge.mjs

browser.close() is only called after the successful completion of the loop. If setContent, screenshot, or file I/O throws, the headless Chromium process is left running. In a CI or batch run this can accumulate zombie processes. Fix: wrap the render loop in try/finally to ensure await browser.close() always executes.

🟠 MEDIUM Missing .webp MIME type in dataURI map — scripts/agent-forge.mjs

The MIME map only defines .png, .svg, .jpg, .jpeg. If the base-image filter is fixed to accept .webp files, they'd be served with the fallback MIME image/png (line 33), which could cause rendering issues in some browsers. Add '.webp': 'image/webp' to the MIME map.

🟠 MEDIUM No error handling — readFileSync crashes on any missing file — scripts/agent-forge.mjs

readFileSync throws synchronously if a base image or harness logo is missing from disk. No try/catch, no graceful skip, no partial output. The entire batch is lost. Add error handling that logs the missing file and skips the profile, or fails early with a clear message listing which assets are missing.

🟠 MEDIUM Path traversal via --profiles allows reading arbitrary local files — scripts/agent-forge.mjs

When --profiles is provided, buildProfiles returns JSON.parse(readFileSync(PROFILES_PATH)) directly and cardHTML passes profile.base and profile.harness.logo into dataURI(relPath), which calls readFileSync(join(PUB, relPath)). A profile entry with base: '../../../etc/passwd' resolves outside public/ (verified: join(PUB, '../../../etc/passwd') escapes the project). The file content is base64-embedded into the rendered page/screenshot. Fix: validate/whitelist profile paths, reject '..' segments, and ensure paths resolve inside public/.

🟠 MEDIUM Profile fields interpolated into HTML without escaping — scripts/agent-forge.mjs

cardHTML embeds p.harness.name, p.model, p.tools, p.tier, and p.id directly into the generated HTML. With --profiles, an attacker or malformed file can inject tags, break rendering, or execute script in the headless Chromium context. Because the render is screenshot to PNG, this is primarily a correctness/reliability issue, but it also increases attack surface against the browser renderer. Fix: escape HTML entities (<, >, &, ", ') for all interpolated strings.

🟠 MEDIUM Mobile menu CTA text inconsistent with desktop — src/components/Header.astro

Line 22 (desktop): <span class="wf-nav-cta-text">Open console</span> — updated in this PR. Line 40 (mobile menu): <a ...>Launch Agent</a> — unchanged, still reads 'Launch Agent'. Users switching between desktop and mobile see two different CTA labels for the same link. Fix: change line 40 to 'Open console' to match [line 22](https://github.com/tangle-network/tangle-website/blob/993ce13ffa5888c3f4f1ca4fd

🟠 MEDIUM Mobile nav CTA label inconsistent with desktop — src/components/Header.astro

Line 21-22 renames the desktop CTA from 'Launch Agent' to 'Open console', but line 40 still renders 'Launch Agent' for the identical https://ai.tangle.tools link inside the mobile menu panel. This is a user-facing inconsistency across viewports. Fix: change the mobile menu link text on line 40 to 'Open console'.

🟠 MEDIUM Accidental duplicate CSS block and dead variant bloat index.astro — src/pages/index.astro

The products section comment appears at line 793 and again at line 1323. Lines 793-1044 are byte-identical to lines 1323-1574 (verified with diff), meaning the entire products + sandbox CSS is duplicated inside the same style block. After the duplicate, [lines 1575-1802](https://github

🟠 MEDIUM CSS duplication + conflicting overrides in index.astro — src/pages/index.astro

Three issues in the monolithic <style> block: (a) .products-section block duplicated at lines 794-904 and 1324-1434 (111 byte-identical lines). (b) .sb-section block duplicated at lines 906-1044 and 1436-1574 (139 byte-identical lines). Both second copies are dead CSS that add nothing since they're byte-equal — ~250 lines of bloat suggesting an accidental merge duplicate. (c) The .intel-flow, .intel-sticky, and .intel-step classes are redefined in a second intel block ([lines 1576-1801](https://github.com/tangle-network/tan

🟠 MEDIUM Unhandled WebGL failure aborts homepage script and hides content — src/pages/index.astro

The Three.js hero block (lines 387-471) calls new THREE.WebGLRenderer(...) inside if (knotCanvas && !reduced && wide) without a try/catch. If WebGL is unavailable (headless screenshot tools, disabled GPU, etc.), WebGLRenderer throws and aborts the entire module before lines 476-575 run. That prevents html.js from being added, the reveal IntersectionObserver from running, and flow.dataset.stage / .sb-tile.on from being set. Because .istage defaults to opacity: 0 and only becomes visible via intel-flow[data-stage=...]

🟡 LOW 5 SVG files missing trailing newline — public/images/harness/opencode.svg

opencode.svg, claude-code.svg, github.svg, linear.svg, and slack.svg all lack a final LF byte (verified via hex dump of last byte — all end with '>' (0x3e) instead of newline). pi.svg is clean. This can cause spurious diff churn and breaks POSIX line conventions. Add a trailing newline to each.

🟡 LOW OpenCode SVG has redundant nested wrapper — public/images/harness/opencode.svg

The file wraps a 512x512 SVG inside another 512x512 SVG, declares an unused xmlns:xlink namespace, and includes a no-op prefers-color-scheme stylesheet. It renders, but it is unnecessarily complex and slightly larger than a flat SVG. Suggest flattening to a single SVG.

🟡 LOW opencode.svg has nested SVG, dead style, and unused namespace — public/images/harness/opencode.svg

Line 1 nests an inner inside an outer (both 512×512). The outer declares xmlns:xlink="http://www.w3.org/1999/xlink" (never used) and version="1.1" (deprecated). Lines 5–7 carry a <style> block targeting :root with prefers-color-scheme media queries — but this SVG is loaded via

🟡 LOW Unreferenced product screenshots — public/images/product/sandbox-full.webp

grep across the repo found zero references to sandbox-full.webp or sandbox-workspace.webp. These two files add ~200KB to the build and appear to be dead assets. Either wire them into a page/JSON or remove them before merge.

🟡 LOW Biased tool shuffle produces non-uniform permutations — scripts/agent-forge.mjs

[...TOOLS].sort(() => r() - 0.5) uses a comparator returning -0.5..0.5, which is a well-known biased shuffle that does not produce uniformly random permutations and can make some orderings impossible. For a seeded, deterministic generator this undermines reproducibility claims. Fix: implement an in-place Fisher-Yates shuffle using the seeded PRNG.

🟡 LOW Fallback regex /./ matches everything including empty string — scripts/agent-forge.mjs

The fallback pick(/./, '', flat) uses regex /./ which matches any single character. For a true match-anything intent, /.+/ (one or more) or /./ (any char) both work on non-empty strings, but /./ would also match on files with no name if they existed. Minor — the fallback is already operating on an empty array in the current state, and correcting the webp filter would make this path unreachable.

🟡 LOW No tests for the script — scripts/agent-forge.mjs

This is a new 178-line script with deterministic RNG logic, tier mappings, HTML rendering, and filesystem I/O. No test file exists. The deterministic PRNG design makes this highly testable — mulberry32 output, tier skew distribution, and profile shape can all be asserted. A basic smoke test would have caught the .png/.webp mismatch immediately.

🟡 LOW Unused export VIDEO_MOTION — src/data/briefs-v2.mjs

VIDEO_MOTION is exported but not imported by any file in the repository (grep across entire worktree). It has no corresponding entries in art-review.json and no consumer. If this is intentional (future video generation round), add a comment. Otherwise remove to avoid confusion.

🟡 LOW Inter font-weight 800 not loaded but 800-weight rules exist elsewhere — src/layouts/BaseLayout.astro

Removing Manrope (which provided weight 800) is correct since Manrope is retired. However, global.css still uses font-weight: 800 at lines 772, 1178, and 2664 on elements whose font-family resolves to Inter. The Google Fonts URL loads Inter at 400/500/600/700 — weight 800 is not loaded. Elements using font-weight:800 with Inter will degrade to 700 (nearest loaded weight) rather than rendering at true 800. This is not a regression introduced by this PR (Inter 800 was never loaded), but the Manrope removal makes the gap more visible since Manrope was the only source of an 800-weight font in the old URL. If 800-weight rendering matters on those global.css eleme

🟡 LOW Table content clipped at desktop widths instead of scrollable — src/layouts/BlogLayout.astro

The table styling changed from display: block; overflow-x: auto; (always scrollable) to display: table; overflow: hidden; at viewports >640px. If a blog post contains a table with columns that exceed the 62rem article container, overflow content will be clipped with no scroll affordance. The old pattern provided horizontal scrollbars as a safety net at all breakpoints. The new pattern only provides scrolling at ≤640px (line 418-432). Impact: any blog post with a wide table (many columns, long unbreakable content like code snippets or URLs) will silently clip data at desktop. Fix: change overflow: hidden to overflow-x: auto, or wrap the table in a

🟡 LOW Slug function mismatch between blog index and series route — src/pages/blog/index.astro

blog/index.astro:13 uses .replace(/^-|-$/g, '') to strip leading/trailing hyphens, while blog/series/[series].astro:15 uses .replace(/^-+|-+$/g, ''). The mismatch means a series name that double-dash-separates (e.g. '--foo--') would produce different slugs: blog/index would link to -foo- while getStaticPaths would generate foo. No existing series names trigger this, but it's a latent inconsistency. Fix: extract a shared slugify utility or align the regex.

🟡 LOW Dead code: sandboxSnippet defined but unused — src/pages/index.astro

const sandboxSnippet = … is defined at line 61 but never referenced in any JSX expression or script block in the file. It appears to be leftover from an earlier design iteration that showed a code snippet in the page.

🟡 LOW Permanent 301 redirect used for a temporarily dormant page — src/pages/research.astro

The comment says the research index is dormant until real studies exist and will return as a publications index later, but the redirect is return Astro.redirect('/blog/series/the-self-improving-stack', 301). A 301 is cached permanently by browsers and search engines, making it hard to restore /research when papers land. In Astro static output the HTTP status is also implemented as a meta-refresh, so the 301 semantic is not actually honored at the server layer. Fix: change the status to 302 or 307 to reflect the temporary nature of the dormancy.

🟡 LOW Dead .wf-h1 line-height change — src/styles/global.css

The first .wf-h1 block (line 2192-2201) sets line-height: 0.95, but a later .wf-h1 block at line 3319-3327 uses line-height: var(--type-h1-line) !important; (1.02). Because the later rule has !important and appears later in the cascade, the 0.95 value never applies. The PR changes this value from 0.9 to 0.95 but it remains ineffective. Either drop the line-height declaration from the first block or align it with --type-h1-line.

🟡 LOW Duplicate --editorial-paper declaration in :root — src/styles/global.css

Line 226 sets --editorial-paper: #f4f4f9 (pre-existing), line 250 sets --editorial-paper: var(--paper-0) (new, wins). The #f4f4f9 value is dead code. Remove line 226 or consolidate to avoid confusion during future migrations.

🟡 LOW Inconsistent line-height between .wf-h1 fallback and hero override — src/styles/global.css

Sandbox-ui .wf-h1 (L2197) uses hardcoded line-height:0.95. Hero .wf-h1 (L3323) uses var(--type-h1-line) which resolves to 1.02. Hero wins via !important, so the 0.95 never renders. Consider using var(--type-h1-line) in both blocks for consistency, or dropping the unused value.

🟡 LOW Redundant --editorial-* declarations in same :root block — src/styles/global.css

Lines 226-230 define --editorial-paper, --editorial-ink, and --editorial-nav-bg, then lines 250-252 redefine the same three variables in the same :root rule. The first set is dead code and will confuse maintainers. Consolidate by keeping only the second set.


tangletools · 2026-07-09T15:24:33Z · trace

@tangletools tangletools 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.

❌ 2 Blocking Findings — 993ce13f

Full multi-shot audit completed 8/8 planned shots over 130 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 8/8 planned shots over 130 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-09T15:24:33Z · immutable trace

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.

2 participants