Skip to the content

Capsid menu

app/lib/social/lint.ts

27 lines and 2 functions in DrDustinEdwards/carrel, at commit d5d5e82 on 2026-10-24. This file on GitHub.

Functions

The functions of lint.ts
FunctionLinesComplexityCoveredCRAPMutation
length15 to 182100%OkCRAP 2No data: mutation testing has not run on this repo yet
lintPost22 to 33886%WatchCRAP 8.2No data: mutation testing has not run on this repo yet

Imports

It imports app/lib/novels/checks.ts, app/lib/social/platforms.server.ts. Imported by app/lib/social/queue.server.ts.

Source

// The AI-habits lint on every post (design section 5, "Social"), and the checks a post needs before
// it can go: it fits the platform, it is not a reply in disguise, it is not empty. A post with any
// finding is held for Dustin; the lint flags and he decides.
//
// The habits are the shared check's (site-runtime ./prose) with stage 4's words on top (SEED_WORDS, the
// AI-tells list first kept in capsid/conventions.md): one check for books, posts and drafts.

import { checkProse } from "@dustinedwards/site-runtime/prose";

import { SEED_WORDS } from "~/lib/novels/checks";

import { graphemes, type Platform } from "./platforms.server";

/** X counts every link as 23 characters, however long; Bluesky counts graphemes. */
function length(text: string, platform: Platform): number {
  if (platform === "bluesky") return graphemes(text);
  return [...text.replace(/https?:\/\/\S+/g, "x".repeat(23))].length;
}

const LIMIT: Record<Platform, number> = { bluesky: 300, x: 280 };

export function lintPost(text: string, platform: Platform): string[] {
  const out: string[] = [];
  const t = text.trim();
  if (!t) return ["The post is empty."];
  const n = length(t, platform);
  if (n > LIMIT[platform]) out.push(`${n} characters, over ${platform === "x" ? "X" : "Bluesky"}'s ${LIMIT[platform]}.`);
  // A post that opens on a handle is threaded as a reply by readers, and replies are never Carrel's.
  if (/^[.\s]*@\w/.test(t)) out.push("It opens with an @handle, which reads as a reply.");
  for (const issue of checkProse(t, { phrases: SEED_WORDS })) out.push(issue.message);
  if (/!\s*$/.test(t) && /!.*!/.test(t)) out.push("More than one exclamation mark.");
  return out;
}