Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit032ae5f

test: fix 12 false-failing tests — line endings, Windows paths, stale landing markup

test: fix 12 false-failing tests — line endings, Windows paths, stale landing markup

Suite was 22 red on a Windows checkout; none were product bugs. Fixed the
real, permanent causes:

- .gitattributes: force LF on .claude/skills/**/*.md (+ AGENTS.md, llms.txt).
  CRLF broke strict frontmatter parsers (Claude harness + our tests). Cleared
  6 skill-frontmatter failures. Normalized the 3 committed SKILL.md files.
- Windows path assertions: normalize separators in git-repository (bare-repo
  path), claude-web-session (sessionWorkdir), and self-host (fake fsExists key)
  tests. They asserted POSIX paths against platform-native join() output.
- Landing hero tests (u-polish): rewrote against the LIVE view. Home `/`
  renders LandingProPage (landing-pro.tsx, `lp-hero-*`); the tests still
  asserted a retired "2030 reboot" hero (hero-actions/hero-trust/hero-card)
  that no longer exists.
- Added a `typecheck` script (tsc --noEmit) as a target. Note: the codebase
  has pre-existing type errors (Bun transpiles without checking); cleaning
  them is tracked separately — not yet a blocking gate.

Suite now 2934 pass / 4 fail. The remaining 4 pass in isolation and fail only
from cross-file Bun mock.module bleed (~14 files mock ../db at module scope
without restore) — a pre-existing test-infra issue, not a product defect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ccanty labs committed on July 13, 2026Parent: 8fcfd61
6 files changed+3518032ae5fb354789ced6fa6aa9e9c97992a43320c8
6 changed files+35−18
Modified.gitattributes+8−0View fileUnifiedSplit
11# Shell scripts must stay LF — a CRLF shebang line fails on Linux with
22# "bad interpreter: /usr/bin/env bash^M". These run on the VPS.
33*.sh text eol=lf
4
5# Claude skill/agent files are parsed by strict LF-expecting frontmatter
6# parsers (both the Claude Code harness and our own tests). A CRLF checkout
7# on Windows breaks `name:`/`description:` extraction. Force LF everywhere.
8.claude/skills/**/*.md text eol=lf
9*.SKILL.md text eol=lf
10AGENTS.md text eol=lf
11llms.txt text eol=lf
Modifiedpackage.json+1−0View fileUnifiedSplit
1818 "db:migrate": "bun run src/db/migrate.ts",
1919 "db:studio": "drizzle-kit studio",
2020 "test": "bun test",
21 "typecheck": "tsc --noEmit",
2122 "preflight": "bun scripts/preflight.ts",
2223 "e2e": "playwright test --config=e2e/playwright.config.ts"
2324 },
Modifiedsrc/__tests__/claude-web-session.test.ts+3−1View fileUnifiedSplit
5959 it("reads from env at call time", () => {
6060 expect(claudeWebRoot()).toBe("/tmp/cw-test");
6161 expect(claudeBinary()).toBe("claude-test");
62 expect(sessionWorkdir("abc")).toBe("/tmp/cw-test/abc");
62 // sessionWorkdir joins with the platform separator; on the Linux server
63 // it is POSIX. Normalize so the assertion is stable on a Windows checkout.
64 expect(sessionWorkdir("abc").replace(/\\/g, "/")).toBe("/tmp/cw-test/abc");
6365 });
6466
6567 it("defaults sensibly when env unset", () => {
Modifiedsrc/__tests__/git-repository.test.ts+3−1View fileUnifiedSplit
3535
3636 it("should initialize a bare repository", async () => {
3737 const path = await initBareRepo(owner, repo);
38 expect(path).toContain(`${owner}/${repo}.git`);
38 // Normalize separators — initBareRepo returns platform-native paths
39 // (backslashes on Windows), so compare on a POSIX-normalized form.
40 expect(path.replace(/\\/g, "/")).toContain(`${owner}/${repo}.git`);
3941 expect(await repoExists(owner, repo)).toBe(true);
4042 });
4143
Modifiedsrc/__tests__/self-host.test.ts+3−1View fileUnifiedSplit
399399 // pair is treated as a no-op; tests assert on `calls` instead.
400400 return { ok: true, stdout: "", stderr: "", exitCode: 0 };
401401 },
402 fsExists: (p: string) => fsExistsMap[p] === true,
402 // Normalize separators: the orchestrator builds paths with the platform
403 // join (backslashes on a Windows checkout), but the map keys are POSIX.
404 fsExists: (p: string) => fsExistsMap[p.replace(/\\/g, "/")] === true,
403405 fsMkdir: async (_p: string, _opts?: any) => undefined,
404406 fsWrite: async (p: string, body: string) => {
405407 writes.push({ path: p, body });
Modifiedsrc/__tests__/u-polish.test.ts+17−15View fileUnifiedSplit
3838 return await res.text();
3939}
4040
41describe("Block U1 — landing hero (2030 reboot)", () => {
42 it("renders exactly two CTAs in the hero actions row", async () => {
41describe("landing hero — primary CTA contract", () => {
42 // Home `/` renders LandingProPage (src/views/landing-pro.tsx). These
43 // assert the live hero contract (`lp-hero-*`), replacing the retired
44 // "2030 reboot" markup (hero-actions / hero-trust / hero-card).
45 it("renders the primary CTAs in the hero row", async () => {
4346 const body = await fetchHomeHtml();
44 const start = body.indexOf('class="hero-actions');
47 const start = body.indexOf('class="lp-hero-ctas"');
4548 expect(start).toBeGreaterThan(-1);
46 // The actions row is a tight window of <a … class="btn …"> anchors.
47 const tail = body.slice(start, start + 600);
48 const anchorMatches = tail.match(/<a[^>]*class="btn[^"]*"/g) || [];
49 const tail = body.slice(start, start + 400);
50 expect(tail).toContain('href="/register"'); // Sign up free
51 expect(tail).toContain('href="/import"'); // Migrate from GitHub
52 const anchorMatches = tail.match(/<a[^>]*class="lp-btn[^"]*"/g) || [];
4953 expect(anchorMatches.length).toBe(2);
50 expect(tail).toContain('href="/register"');
51 expect(tail).toContain('href="#loop"');
5254 });
5355
54 it("renders the trust line beneath the CTAs", async () => {
56 it("renders the hero links row beneath the primary CTAs", async () => {
5557 const body = await fetchHomeHtml();
56 const ctas = body.indexOf('class="hero-actions');
57 const trust = body.indexOf('class="hero-trust');
58 const ctas = body.indexOf('class="lp-hero-ctas"');
59 const links = body.indexOf('class="lp-hero-links"');
5860 expect(ctas).toBeGreaterThan(-1);
59 expect(trust).toBeGreaterThan(ctas);
60 expect(body).toContain("Self-hosted · Git-native · Claude-first");
61 expect(links).toBeGreaterThan(ctas);
62 expect(body.slice(links, links + 200)).toContain('href="/demo"');
6163 });
6264
6365 it("places the product-card mock below the CTAs", async () => {
6466 const body = await fetchHomeHtml();
65 const ctas = body.indexOf('class="hero-actions');
66 const card = body.indexOf('class="hero-card');
67 const ctas = body.indexOf('class="lp-hero-ctas"');
68 const card = body.indexOf('class="lp-hero-card"');
6769 expect(ctas).toBeGreaterThan(-1);
6870 expect(card).toBeGreaterThan(ctas);
6971 });
7072