Vibe Coding Day-to-Day: A Week in My Git Log, the 10% I Never Delegate, and Which Tool Actually Does What

The Feature Ticket I Opened Monday Morning (And Exactly What Happened Next)

## The Feature Ticket I Opened Monday Morning (And Exactly What Happened Next)

Here’s the prompt I dropped into Cursor at 9:04 AM:

Add a WishlistButton.tsx component that calls useCartUpdate.ts on toggle.
Button should be optimistic — update UI immediately, roll back on 400/500.
useCartUpdate already handles auth headers. Don’t duplicate that logic.
Wire the loading state to disable the button during the in-flight request.

Cursor (backed by Claude 3.7 Sonnet) returned this in about 12 seconds:

tsx
// WishlistButton.tsx
import { useState } from ‘react’;
import { useCartUpdate } from ‘../hooks/useCartUpdate’;

interface WishlistButtonProps {
productId: string;
initialState: boolean;
}

export function WishlistButton({ productId, initialState }: WishlistButtonProps) {
const [isWishlisted, setIsWishlisted] = useState(initialState);
const { mutate, isLoading } = useCartUpdate();

const handleToggle = async () => {
const previous = isWishlisted;
setIsWishlisted(!isWishlisted); // optimistic update

mutate(
{ productId, wishlisted: !previous },
{
onError: () => setIsWishlisted(previous), // rollback
}
);
};

return (

);
}

Structurally correct. The optimistic pattern is right, the rollback is wired up, and it didn’t re-implement auth header logic from `useCartUpdate`. The one thing I changed before committing: the `aria-pressed` attribute was using `isWishlisted` directly, which outputs `true`/`false` as a boolean in JSX. Screen readers expect the string literals `”true”` and `”false”`. Changed it to `aria-pressed={isWishlisted ? “true” : “false”}`. That’s a small thing. It’s also the kind of small thing that doesn’t show up in visual testing and won’t surface in a code review where the reviewer is moving fast.

That edit took 30 seconds. The component took 12 seconds. That ratio is roughly what a good Monday morning looks like.

Which brings me to the actual thesis of this piece, because I want to name it early rather than bury it at the end: vibe coding is a multiplier on throughput, and it has a hard ceiling. The ceiling sits at roughly 10% of the code in any production feature — the parts where you’re encoding business rules, security assumptions, or user safety guarantees. That code is not a candidate for delegation. Not to Claude, not to any current model. I’ll get specific about what falls into that category later in this piece, with examples from the same week.

Stack this week, no hedging:

**Cursor** as the primary editor. This is where most of the generation happened — the component scaffolding, the hook plumbing, the test boilerplate. Cursor’s tab completion and inline diff workflow is fast enough that context-switching out to a separate chat window feels like a penalty.

**Claude API directly** for cross-file reasoning tasks. When I needed to ask “given the shape of `useCartUpdate`, `CartContext`, and `CheckoutFlow.tsx`, where is the most likely place a double-submit could happen?” — that’s a question that benefits from pasting the relevant files and thinking through the answer in a longer-form chat. Cursor’s composer handles some of this, but I’ve found the API directly (via a simple shell script that pipes files into a prompt) gives me cleaner answers when the question is architectural rather than syntactic.

On versions: I’m using Claude 3.7 Sonnet for anything requiring multi-step reasoning and 3.5 Haiku for quick lookups and single-function generation where latency matters more than depth. If you’re reading specs from late 2024 about 3.5 Sonnet’s context window — those don’t apply to the current production variants. Check the current Anthropic docs before making architecture decisions based on context limits.

A Week in My Git Log

## A Week in My Git Log

The annotation convention I use is simple: every commit message gets an `[AI: ~X%]` tag at the end, where X is a rough estimate of how much of the final diff came from model output with no manual editing. Anything I rewrote substantially is `~0%` even if I used the model as a starting point. Anything I accepted verbatim or with only whitespace fixes is `~95%+`. The percentage lives in the commit message, not a separate log, so `git log –oneline` tells the story at a glance.

Whether your team adopts this convention is a separate conversation — there are real arguments against it (noise, bikeshedding over the number, management misreading it as a productivity score). I use it personally because it forces me to be honest with myself about where I actually spent cognitive work. More on team rollout later.

Here’s a representative week. The feature was a wishlist system bolted onto an existing e-commerce backend: save items, share via link, notify on price drop. Straightforward scope, enough surface area to hit every category of AI-assist pattern I care about.

**`feat: scaffold wishlist API route [AI: ~80%]`**
*~25 minutes total. Human contribution: wrote the prompt, reviewed the generated handler, deleted one middleware it hallucinated that didn’t exist in the codebase, wired up the real auth middleware by name.*

The model produced a complete Express route file on the first pass. The shape was right. It invented a `validateSession` helper that doesn’t exist in this repo — a classic failure mode where the model knows the pattern but not your specific exports. Caught on first read, fixed in two minutes. The remaining 20% was knowing which real function to substitute.

**`feat: wishlist data model + migration [AI: ~60%]`**
*~40 minutes total. Human contribution: reworked the schema to match existing naming conventions (`created_at` not `createdAt`), added a composite index the model missed, reviewed the migration for destructive operations.*

Model output was structurally fine but had three column names that didn’t match the rest of the schema. This is the pattern where AI is faster than typing from scratch but slower than just being consistent in the first place. The missed index mattered — a `(user_id, item_id)` uniqueness constraint the model would have included if I’d mentioned it in the prompt. Prompt quality problem, not model quality problem.

**`feat: share link generation + short URL logic [AI: ~90%]`**
*~15 minutes total. Human contribution: verified the collision probability reasoning was correct, changed the token length from 8 to 12 characters after thinking about the projected user volume.*

This was the cleanest AI output of the week. Stateless, self-contained, easy to reason about. When there’s no project-specific context to know and the problem is well-defined, model output is nearly ready to ship. The only real decision was token length — the model defaulted to 8, which is fine at small scale but I prefer more headroom.

**`fix: rewrite auth guard by hand [AI: 0% — see The 10% section]`**
*~90 minutes total. Human contribution: everything.*

The model’s auth guard looked plausible for three separate reads. It passed a token check but had a subtle flaw in how it handled expired JWTs with the `iat` claim under clock skew conditions. I caught it writing a unit test for the failure case. Once I saw the actual bug I didn’t trust any model-generated path through the auth flow. Rewrote the whole thing manually, wrote six explicit test cases, commented the edge case inline. This is exactly the category of work that should never be delegated. Full explanation in the next section.

**`refactor: rename props across 14 files [AI: ~95%]`**
*~10 minutes total. Human contribution: wrote the prompt listing old and new names, spot-checked 3 of 14 files, ran the test suite.*

This is where AI-assisted editing is genuinely useful with no real downside. Mechanical, high-blast-radius, boring to do manually, easy to verify. The model got all 14 files correct. The one thing worth noting: it also renamed a variable inside a comment that was intentionally using the old name as historical documentation. I reverted that single hunk. Edge case, caught in review, not a problem.

**`test: price drop notification integration tests [AI: ~50%]`**
*~35 minutes total. Human contribution: wrote the assertions for the failure cases myself, rewrote two test descriptions that were misleading about what was actually being tested.*

The model writes happy-path tests confidently and edge-case tests poorly. The generated test for “item not found” was actually testing the wrong layer — it was mocking at the wrong depth and would have passed even if the feature was broken. This is a common failure mode in AI-generated test suites. I now write all failure-case assertions by hand and use model output only for the scaffolding and the happy paths.

**`docs: update API reference for wishlist endpoints [AI: ~85%]`**
*~20 minutes total. Human contribution: corrected two parameter descriptions that were subtly wrong, added the error response format which the model omitted.*

### The Honest Before/After

The patterns that held across this week — and across repeated feature work before it:

**Accepted as-is:** Self-contained, stateless logic with no project-specific dependencies. String manipulation, simple data transformations, boilerplate route scaffolding where I described the shape explicitly. The model is fast here and the verification cost is low.

**Rejected and rewritten:** Anything touching auth, session state, or security-adjacent logic. Anything where the model had to infer project conventions it wasn’t shown. Test cases for failure paths. The rework cost in these cases is usually 30–90 minutes — not because the model output was obviously wrong, but because it was plausibly wrong, which requires the slower kind of reading.

**Accepted with targeted edits:** Most of the week falls here. The model got the structure right, I fixed the details. The cognitive pattern is less “write code” and more “review and patch code.” That’s a different skill than pure authorship, and it has its own failure modes — primarily, the tendency to accept things that look right because reviewing is cognitively lighter than writing.

The time question is genuinely hard to answer honestly. I don’t track hours per task. What I can say is that the scaffolding commits — the ones sitting at 80-95% — felt faster. The debugging commits — the ones that ended at 0% — did not. The aggregate might net out to faster, or it might not. The more defensible claim is that the distribution of effort shifted: less time on structure and boilerplate, more time on reasoning about correctness in the parts that matter.

What Vibe Coding Actually Means in Practice

## What Vibe Coding Actually Means in Practice

You write intent, the model writes code, you review and steer. That’s it. Skip the philosophy.

### .cursorrules and .cursorignore: Configure Both or Regret It

Most tutorials cover `.cursorrules`. Almost none cover `.cursorignore`. This is why people end up with an AI that “helpfully” rewrites their Alembic migration files or starts suggesting changes inside `vendor/` directories.

**`.cursorrules`** sits at your project root and tells Cursor how to behave — tone, stack conventions, patterns you want enforced. A minimal example:

– Use Python 3.11+ type hints everywhere
– Prefer early returns over nested conditionals
– Never use `assert` for runtime checks in production paths
– SQLAlchemy models live in models/, not inline in routes

Keep it specific. Vague rules (“write clean code”) do nothing. Rules that reference actual project decisions (“use `httpx.AsyncClient`, not `requests`”) actually constrain output.

**`.cursorignore`** is where most setups break down. It works like `.gitignore` — glob patterns, one per line — and tells Cursor which files to exclude from context and suggestion entirely. Without it, the model sees your entire working tree as fair game.

Files that should always be in `.cursorignore`:

# Generated files
**/migrations/*.py
**/alembic/versions/*.py

# Vendored dependencies
vendor/
third_party/

# Build artifacts
dist/
build/
*.min.js
*.min.css

# Auto-generated protobuf / GraphQL
**/*_pb2.py
**/generated/
schema.graphql

What happens without this file: you ask for a feature, the model decides your 2021 migration file has a naming inconsistency, and it suggests a “cleanup.” You accept it without reading closely. Now your migration history is broken and `alembic history` lies to you. This is not a hypothetical — it’s a predictable failure mode whenever the model has too much context with no constraints on what it can touch.

Place `.cursorignore` at the project root alongside `.cursorrules`. Both files get committed to version control. New team members inherit the constraints automatically.

### Piping to Claude via the Anthropic API: Exact Setup

The original workflow I described uses a shell script that pipes file content or stdout directly to Claude through the Anthropic API — not through Cursor’s built-in model toggle, and not through any third-party CLI wrapper.

**What you’re actually using:** the [official Anthropic Python SDK](https://github.com/anthropic-ai/anthropic-sdk-python), invoked from a thin shell wrapper. There is no official `anthropic` CLI binary that ships as a standalone tool. If you’ve seen tutorials referencing `anthropic chat` as a command — that’s a third-party wrapper, not something Anthropic ships. Don’t confuse the two.

**Prerequisites before the script works:**

1. Python 3.8+ installed
2. `pip install anthropic` (installs the SDK and its deps)
3. `ANTHROPIC_API_KEY` exported in your environment — put it in `.zshrc` or `.bashrc`, not hardcoded anywhere:
bash
export ANTHROPIC_API_KEY=”sk-ant-…”
4. Confirm it’s live: `python -c “import anthropic; print(anthropic.__version__)”`

**The actual script structure:**

bash
#!/usr/bin/env bash
# ask.sh — pipe any content to Claude and get a response on stdout

MODEL=”claude-opus-4-5”
MAX_TOKENS=4096

PROMPT=$(cat “$1” 2>/dev/null || echo “$1”)

python3 – <The 10% You Should Never Vibe Code

## The 10% You Should Never Vibe Code

Here’s the strongest case for AI-generated code: it looks right. It passes your linter. It matches the patterns already in your codebase. It gets approved in code review because the reviewer is also moving fast and the structure is familiar. Then it fails in production in a way that takes three hours to trace because the failure is silent.

That’s not a random failure mode. It follows a pattern. AI generates code that is *structurally* correct and *semantically* wrong in ways that only surface under specific conditions — expired tokens, duplicate webhook deliveries, a user record that happens to have a null field. The 10% you should never delegate isn’t arbitrary. It maps exactly to the categories where silent semantic errors are either irreversible or exploitable.

**The checklist. One rationale each.**

**Auth middleware and session handling.** AI consistently handles the happy path and misses expiry edge cases. You’ll get signature verification, you won’t get clock skew handling, token rotation on refresh, or explicit rejection of tokens issued before a password reset. Those gaps don’t show up in unit tests that use freshly minted tokens.

**Database migrations.** Irreversible by nature. The model has no idea what your actual production schema looks like, what data is in the column it’s about to drop, or whether the column is referenced by a view you created six months ago directly in the DB console and never committed. It will write a migration that works on your seed data and silently destroys something in prod.

**Encryption and key management logic.** Parameter errors here are invisible until they’re exploited. Wrong IV reuse, ECB mode selected because it was simpler to demonstrate, hardcoded salt, PBKDF2 with an iteration count from a 2015 Stack Overflow answer. The code runs. It encrypts. It just doesn’t protect anything.

**Billing webhooks and payment processing.** Idempotency is the failure surface. AI-generated webhook handlers usually lack idempotency keys, don’t handle duplicate delivery, and don’t account for out-of-order events. A `charge.succeeded` processed twice means a subscription activated twice or a credit applied twice. These bugs are expensive to fix after the fact and nearly invisible in testing because your test environment sends each event exactly once.

**Any code that touches PII logging.** This is the one that’s bitten me the most watching other people’s diffs. AI adds `console.log(user)` or `logger.debug({ body: req.body })` during scaffolding and almost never removes it. The entire request body goes to your log aggregator. If that body contains an SSN, a bank account number, or a password (yes, people still POST passwords in JSON bodies), it’s now in your logging infrastructure, potentially in plaintext, possibly shipped to a third-party service.

**The category of mistake I now grep for in every AI-written auth review.**

JWT validation that checks the signature but skips claim verification. The specific pattern: the code calls the verify function, checks that it didn’t throw, and then reads `decoded.userId` — but never validates `aud`, never checks `iss`, sometimes never checks `exp` because the library default was assumed to handle it (it doesn’t always, depending on options passed).

What to grep for: look for your JWT verify call and then look at what’s *not* there. If you don’t see explicit `audience` and `issuer` options passed to the verify call, or explicit checks on the decoded payload afterward, assume they’re missing. A token signed with your secret but issued by an attacker-controlled service and intended for a different audience will pass signature verification. That’s the attack surface.

The code-smell signature: the verify call is clean and short, the error handling is a single `catch` that returns 401, and nothing downstream checks whether the token was actually meant for this service. It *looks* like security. It’s a template.

Same category applies to migration files — grep for `DROP COLUMN` or `DROP TABLE` without a corresponding `up`/`down` pair, and grep for any migration that adds a NOT NULL column without a default. That’s a migration that will fail on a non-empty table in a way your dev environment will never catch.

Write these once yourself. Review them like they’re the code that will page you at 2am. Because they are.

2026 Tool Decision Matrix

## 2026 Tool Decision Matrix

Ratings are based on daily use across a monorepo with mixed TypeScript/Python services. “Situational” means it works but requires you to set it up correctly or accept meaningful limitations. “Weak” means I’ve been burned enough times to stop reaching for it by default.

| Task Type | Cursor | GitHub Copilot | Windsurf | Claude API Direct |
|—|—|—|—|—|
| Boilerplate generation | Good — *fast* | Good — *inline* | Good — *contextual* | Situational — *overhead* |
| Cross-file refactor | Good — *codebase-aware* | Weak — *file-scoped* | Good — *agent-mode* | Weak — *no-editor* |
| Architecture review | Situational — *prompt-dependent* | Weak — *shallow* | Situational — *verbose* | Good — *reasoning-depth* |
| Debugging a stack trace | Good — *trace-aware* | Situational — *manual-paste* | Situational — *inconsistent* | Good — *systematic* |
| Writing tests for existing code | Good — *reads-impl* | Situational — *misses-edge* | Good — *thorough* | Situational — *no-runner* |
| Code review assistance | Situational — *context-window* | Situational — *surface-level* | Situational — *wordy* | Good — *analytical* |

**A note on pricing before you screenshot this table.** Windsurf’s ownership and pricing structure shifted during 2025 following acquisition activity — whatever tier you read about six months ago may not exist anymore. Check their current pricing page directly before budgeting. Cursor’s pricing has similarly moved; any specific monthly figure floating around forums may be stale. Verify both at source. On the Claude API side: GPT-4o comparisons are architecture and interface comparisons at this point, not training recency comparisons — OpenAI has updated GPT-4o’s knowledge through 2025, so treat any “knowledge cutoff” framing as outdated.

### Use X When Y

– **Use Cursor** when you’re doing the bulk of your feature work inside an editor and want codebase-wide context without copy-pasting files into a chat window. Its Composer mode earns its keep on multi-file changes.
– **Use Claude API direct** when the task is analytical rather than generative — reviewing an architecture doc, reasoning through why a test suite is structurally broken, or producing a decision memo. The absence of an editor integration is a real cost, but the reasoning quality on open-ended problems justifies the friction.
– **Use GitHub Copilot** if your organization already pays for it through an enterprise license and your work is primarily single-file, autocomplete-style generation. Trying to force it into agent-style workflows is where it falls apart.
– **Use Windsurf** if Cursor’s pricing or IDE constraints don’t fit your setup and you need agent-mode behavior — it handles multi-step autonomous edits reasonably well, though it tends toward verbose explanations you’ll learn to skip past.

Rolling This Out to a Team That Didn’t Ask For It

## Rolling This Out to a Team That Didn’t Ask For It

The fastest way to kill adoption is to walk into standup and announce “we’re all using AI now.” Don’t do that.

### Start With the Guardrail File, Not the Tool

A `.cursorrules` file is a much easier sell than convincing anyone to change how they write code. It’s just a config file. Most developers have opinions about naming conventions and test boilerplate anyway — you’re just writing those opinions down in a place that also happens to instruct a language model.

Start with rules nobody will argue about:

– File naming conventions your team already follows informally
– Test boilerplate patterns (describe/it structure, how you set up fixtures, whether you mock at the boundary or inline)
– Import ordering if you don’t already enforce it with a linter

Don’t start with anything that feels like a productivity metric, a style opinion that’s actually contested, or anything that could read as “here’s how AI should write your code.” The goal of version one is that a skeptical senior dev reads the PR and thinks “yeah, this is just documenting what we already do.”

Which brings you to the actual move: get one senior dev to co-author it. Not a blessing after the fact — actual co-authorship. They probably have strong opinions about error handling patterns or how auth middleware should be structured. Those go in the file. Now they’re defending the file because it reflects their thinking.

Commit it with a PR that has a section-by-section explanation. Not “this helps AI generate better code” — just “this is what each rule is enforcing and why.” The AI angle is secondary. The PR is really about making implicit team standards explicit.

**The one non-negotiable:** mandate the guardrail file, not the tool. You don’t care whether your teammate uses Cursor, Copilot, a different editor entirely, or writes everything by hand. The `.cursorrules` file is just documentation that also happens to be machine-readable. Mandating tool usage is a different conversation, one you probably don’t want to have in the same quarter.

### What PR Review Actually Looks Like at 60–70% AI Diff

When a large fraction of a diff is AI-generated, the review process doesn’t change in principle but it changes in where you spend attention.

The happy path is usually fine. AI-generated code handles the expected input, calls the right methods, returns the right shape. That’s not where things go wrong.

**Where to slow down:**

Error handling branches are the first place. A common failure mode is that AI generates a `try/catch` that swallows the error, logs a generic message, and returns a null or empty value — technically handling the exception, practically hiding the failure. Read every error path like you’re looking for a silent failure.

Auth paths are the second. Any file that touches authentication, authorization, session management, or token validation should have explicit human sign-off, full stop. This isn’t because AI is uniquely bad at auth — it’s because auth is where “mostly right” is indistinguishable from “wrong” until someone demonstrates it isn’t. If you’re using a `.cursorignore` list to keep certain files out of AI context, any file on that list that somehow ends up in the diff anyway should require the author to explain in writing why it’s there and what they verified manually.

**AI-generated tests are a floor, not a ceiling.** This is the thing I’d push hardest on. The tests that come out of a typical AI-assisted workflow cover the happy path well, cover a few obvious edge cases, and stop there. They don’t test what happens when an upstream service returns a 429 and your retry logic is wrong. They don’t test what happens when a JWT is structurally valid but expired by one second. They don’t test the race condition in your cache invalidation. Humans write the adversarial cases. The AI tests tell you the feature works when everything goes right; they don’t tell you it fails gracefully when things go wrong.

Practically: in your PR template, add a checkbox that’s something like “adversarial test cases written for [describe what].” Not a mandate to write N tests — a mandate to think about what could go wrong that a language model optimizing for plausible code wouldn’t think to test.

### The Commit Message Disclosure Question

There’s no universal right answer here, so I’ll lay out both positions honestly.

**The transparency position:** If a meaningful portion of the code was generated by a tool, the commit history should say so. Future maintainers reading a blame view deserve to know whether the logic was written by someone who understood the full context or by a model that was completing a prompt. It also creates a paper trail if something goes wrong and you need to trace where a pattern originated.

**The noise position:** Commit messages describe what changed and why, not how the author produced the change. We don’t note that someone used a snippet from Stack Overflow, used an IDE refactor tool, or copied from another file in the project. AI is just another tool in that workflow. Flagging it in metadata adds friction, doesn’t change the review obligation, and creates a two-class system where AI-assisted code is implicitly suspect in a way that other code isn’t.

My actual recommendation: make it a team policy with a clear default, and revisit it in six months. Both positions are defensible. What’s not defensible is leaving it ambiguous so that some teammates are flagging and others aren’t, which gives you the worst of both worlds — inconsistent metadata and ongoing friction about whose practice is correct.

One thing that’s not ambiguous: as of 2026, some organizations — particularly in regulated industries and on government contracts — are starting to require AI disclosure in commit metadata as a compliance requirement, not a style preference. If you’re in one of those contexts, the debate is already settled for you. Check before you establish a “no disclosure” norm that you’ll have to unwind later.

FAQ

## FAQ

**Q: Does this workflow make junior devs dangerous?**

The claim that “juniors ship faster than seniors who refuse this” is true. It’s also incomplete in a way that matters.

The risk isn’t speed. A junior can absolutely ship a working feature faster with AI assistance than a senior working unassisted. The problem surfaces two weeks later when that feature interacts with the auth middleware in a way the junior didn’t anticipate, or when the AI generated a SQL query that works fine on 500 rows and locks the table at 500,000.

Juniors lack pattern recognition for the 10% failure modes — the edge cases that don’t show up in unit tests, the security footguns that look like idiomatic code, the architectural decisions that seem locally correct but create global debt. The AI has no way to compensate for this because it doesn’t know your system’s history, your team’s implicit constraints, or which parts of the codebase are load-bearing in non-obvious ways.

The solution is not “don’t give juniors the tool.” That’s how you create a skills gap that compounds over time.

The actual solution is two things:

First, the pre-flight checklist. Before any AI-assisted PR merges, the author answers specific questions: Does this touch auth? Does this add a dependency? Does this change a shared interface? Does this write to the database schema? Each yes triggers a mandatory senior review of that specific section — not the whole PR, just the flagged component.

Second, `.cursorignore`. Lock juniors out of the files they shouldn’t be touching with AI assistance until they understand those systems well enough to review AI output on them. Core auth logic, payment processing, the ORM base classes, migration files — add them to `.cursorignore` so the AI can’t read or modify them without that configuration being explicitly changed. It’s not permanent. It’s scaffolding while pattern recognition develops.

The dangerous scenario isn’t juniors using this workflow. It’s juniors using it without a structured review gate on the 10% that requires human judgment.

**Q: What happened to the truncated `tail -f` log command in the original post?**

The original post cuts off mid-sentence. The complete command and its context got dropped in editing.

The full command for macOS:

bash
tail -f ~/Library/Logs/Cursor/main.log | grep -i “error\|failed\|timeout”

On Linux (Cursor stores logs under the standard XDG path):

bash
tail -f ~/.config/Cursor/logs/main.log | grep -i “error\|failed\|timeout”

What this does: when an AI request silently fails — no completion, spinner hangs, response truncates — the UI gives you nothing useful. The main log captures the underlying request lifecycle including API errors, rate limit responses, and network timeouts that Cursor doesn’t surface in the editor UI. Running this in a terminal alongside the editor gives you immediate visibility into whether a failure is a Cursor bug, a model API issue, or a local network problem.

The `grep` filter is optional but cuts out the noise from normal operation. If you’re chasing an intermittent failure, drop the grep and watch the raw stream to catch the sequence of events leading up to the error.

Log path occasionally changes between Cursor releases. If the path above returns nothing, check `~/Library/Application Support/Cursor/logs/` on macOS or run `find ~/.config/Cursor -name “*.log” 2>/dev/null` on Linux to locate the current log directory.

**Q: Should I remove the internal link to “Essential SaaS Tools for Small Business”?**

Yes. Remove it.

That link is topically incoherent in the context of a post about senior developer workflows and AI-assisted coding. A reader who just read 2,000 words about `.cursorignore` files and git discipline has no reason to click through to a SaaS tool roundup aimed at small business owners. The audience mismatch is complete.

From a crawler’s perspective, that kind of cross-topic internal linking is a signal associated with content farms trying to distribute link equity across unrelated posts. It undercuts the topical authority you’re building in the developer tooling space.

Replace it with a contextually relevant link if one exists on the site — a local LLM setup guide, a post on self-hosted code completion with something like Ollama and Continue, or anything covering the infrastructure side of running AI tooling yourself. If that post doesn’t exist yet, leave the anchor text out entirely rather than linking somewhere that doesn’t serve the reader. A missing link costs you nothing. A bad one signals something about the editorial standards of everything around it.


Eric Woo

Written by Eric Woo

Lead AI Engineer & SaaS Strategist

Eric is a seasoned software architect specializing in LLM orchestration and autonomous agent systems. With over 15 years in Silicon Valley, he now focuses on scaling AI-first applications.

Leave a Comment