SignalRank is an AI visibility auditing platform that analyzes how AI assistants may understand, classify, and recommend companies using structured analysis and deterministic scoring.
Architecture: docs/ARCHITECTURE.md
Note: This project was built as a take-home assessment. Live analysis requires an OpenAI API key. If no key is configured, the app returns a safe fallback audit so the UI remains demoable end-to-end.
SignalRank helps answer one question:
“If a user asks an AI assistant for a recommendation in this category, would this company be understood and recommended clearly?”
The app supports two input modes:
- URL mode — fetches readable landing page content using Cheerio.
- Text mode — analyzes pasted copy directly.
It then produces a structured audit containing:
- AI interpretation of the company
- likely category and competitors
- possible misunderstandings
- missing context
- dimension-level scoring
- prioritized findings
- rewrite suggestions
- answerable and unanswerable customer questions
As more users discover products through AI assistants, company positioning needs to be legible not only to humans, but also to LLMs.
A landing page may look polished while still failing to clearly communicate:
- what category the company belongs to
- who it is for
- why it is differentiated
- what evidence supports its claims
- whether AI systems can cite or summarize it confidently
SignalRank simulates that interpretation layer and turns it into a practical content audit.
User input
├── URL mode
│ └── Cheerio content extraction
└── Text mode
└── Text cleanup and truncation
Cleaned content
↓
LLM interpretation and audit generation
↓
Zod response validation
↓
Server-side weighted scoring
↓
Structured results dashboard
Core flow:
- User submits a URL or pasted text.
- The backend extracts and cleans the content.
- The app sends a structured analysis prompt to the LLM.
- The response is validated with Zod.
- The final overall score is computed server-side using weighted dimensions.
- The dashboard renders findings, scores, rewrites, and answerability analysis.
- Used a single structured LLM call to keep latency, cost, and implementation complexity low.
- Computed the final overall score server-side instead of trusting the model to assign it directly.
- Used Zod schemas to validate model output before rendering results.
- Added a safe fallback response so the UI remains demoable without an API key or during LLM failures.
- Avoided persistence to keep the take-home focused on audit quality, product clarity, and implementation speed.
- Designed rewrites to improve clarity without inventing unsupported metrics, customers, certifications, or proof points.
- Separated prompt logic, scoring utilities, URL extraction, fallback responses, and UI components to keep the system maintainable.
| Dimension | Weight | Purpose |
|---|---|---|
| Category Clarity | 25% | Can an AI system understand what the company does? |
| Recommendation Context | 25% | Is it clear when and why the company should be recommended? |
| Differentiation | 20% | Is the company distinguishable from alternatives? |
| Trust Evidence | 15% | Are there proof points that support the claims? |
| Citability | 15% | Can the content be summarized and cited confidently? |
The overallScore is computed server-side from dimension scores. The LLM does not directly control the final score.
src/
├── app/
│ ├── analyze/page.tsx # Analyze tool UI
│ ├── api/analyze/route.ts # POST /api/analyze
│ ├── page.tsx # Landing page
│ └── layout.tsx
├── components/
│ ├── SignalRankHome.tsx # Input, loading, and results phases
│ ├── landing/ # Marketing sections
│ └── signalrank/ # Results dashboard and scoring UI
└── lib/
├── analysis-schema.ts # Zod request/response schemas
├── analysis-prompt.ts # Structured LLM prompt
├── analyze-content.ts # OpenAI integration and validation retry
├── score-utils.ts # Server-side weighted scoring
├── fallback-response.ts # Graceful degradation response
├── url-extract.ts # URL content extraction with Cheerio
└── text-cleanup.ts # Input cleanup and truncation
Request:
{
"mode": "url",
"input": "https://example.com"
}or:
{
"mode": "text",
"input": "Company landing page copy..."
}Returns a structured audit containing:
overallScoresummaryaiInterpretationdimensionsfindingsrewritechangeExplanationsanswerableQuestions
npm install
cp .env.example .env.local
npm run devOpen:
http://localhost:3000
Analyze page:
http://localhost:3000/analyze
| Variable | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes, for live analysis | OpenAI API key used by /api/analyze |
AUDIT_DEBUG |
No | Set to 1 to log prompts, raw LLM output, and validation details |
If OPENAI_API_KEY is missing or the LLM call fails, the API returns a safe fallback audit so the UI still works end-to-end.
npm run dev # Start development server
npm run build # Production build
npm run lint # Run ESLintOptional audit comparison runner:
npx tsx scripts/audit-comparison-runner.tsRequires OPENAI_API_KEY.
This project intentionally prioritizes a polished audit experience over production platform features.
Current limitations:
- No authentication.
- No persistence or audit history.
- No full-site crawling.
- No scheduled re-audits.
- Single-model implementation using OpenAI.
- One URL or text block per audit.
- Live analysis requires an API key.
These were deliberate scope cuts for a take-home project focused on product judgment, AI integration, structured outputs, and clear technical tradeoffs.
- Multi-model comparison across ChatGPT, Claude, and Gemini.
- Competitor benchmarking against named alternatives.
- Audit history with before/after tracking.
- Scheduled monitoring when landing page content changes.
- Website-wide crawling across multiple pages.
- Browser extension for in-context page audits.
- Evaluation harness for testing prompt/model changes.
- Team workspaces and shared reports.
- Next.js 15 App Router
- TypeScript
- OpenAI SDK
- Zod
- Cheerio
- Tailwind CSS
- Lucide icons



