brainstorming
Structured design dialogue that validates ideas before implementation begins.
linear-cost-tuning
Optimize Linear API usage, reduce unnecessary calls, and maximize
Full skill instructions
Optimize Linear API usage to stay within rate budgets and minimize infrastructure costs. Linear's API is free (no per-request billing), but rate limits (5,000 requests/hour, 250,000 complexity/hour) constrain throughput. Efficient patterns let you do more within these limits.
| Factor | Budget Impact | Optimization |
|---|---|---|
| Request count | 5,000/hr limit | Batch operations, coalesce requests |
| Query complexity | 250,000/hr limit | Flat queries, small page sizes |
| Payload size | Bandwidth + latency | Select only needed fields |
| Polling frequency | Wastes budget | Replace with webhooks |
| Webhook volume | Processing costs | Filter by event type and team |
import { LinearClient } from "@linear/sdk";
class UsageTracker {
private requests = 0;
private totalComplexity = 0;
private startTime = Date.now();
track(complexity: number) {
this.requests++;
this.totalComplexity += complexity;
}
report() {
const elapsedHours = (Date.now() - this.startTime) / 3600000;
return {
requests: this.requests,
requestsPerHour: Math.round(this.requests / elapsedHours),
totalComplexity: this.totalComplexity,
complexityPerHour: Math.round(this.totalComplexity / elapsedHours),
budgetUsed: {
requests: `${Math.round((this.requests / elapsedHours / 5000) * 100)}%`,
complexity: `${Math.round((this.totalComplexity / elapsedHours / 250000) * 100)}%`,
},
};
}
}
const tracker = new UsageTracker();
The single biggest optimization. A polling loop checking every minute uses 1,440 requests/day. A webhook uses zero.
// BAD: Polling every 60 seconds (1,440 req/day, ~60 req/hr)
setInterval(async () => {
const issues = await client.issues({
first: 100,
filter: { updatedAt: { gte: lastCheck } },
});
await syncIssues(issues.nodes);
lastCheck = new Date().toISOString();
}, 60000);
// GOOD: Webhook receives updates in real-time (0 requests for monitoring)
app.post("/webhooks/linear", express.raw({ type: "*/*" }), (req, res) => {
// Verify signature, process event
const event = JSON.parse(req.body.toString());
if (event.type === "Issue") {
syncSingleIssue(event.data);
}
res.json({ ok: true });
});
// BAD: ~12,500 pts — deeply nested with large page
// issues(50) * (labels(50 default) * fields + comments(50) * user)
const expensive = `query {
issues(first: 50) {
nodes {
id title
assignee { name }
labels { nodes { name } }
comments(first: 10) { nodes { body user { name } } }
}
}
}`;
// GOOD: ~55 pts — flat fields only
const cheap = `query {
issues(first: 50) {
nodes { id identifier title priority estimate }
}
}`;
// Fetch relations separately only when needed
const issueDetail = `query($id: String!) {
issue(id: $id) {
id identifier title description priority
assignee { name email }
state { name type }
labels { nodes { name color } }
}
}`;
Deduplicate concurrent identical requests.
const inflight = new Map<string, Promise<any>>();
async function coalesce<T>(key: string, fn: () => Promise<T>): Promise<T> {
if (inflight.has(key)) return inflight.get(key)!;
const promise = fn().finally(() => inflight.delete(key));
inflight.set(key, promise);
return promise;
}
// 10 concurrent requests for same team = 1 actual API call
async function getTeam(teamKey: string) {
return coalesce(`team:${teamKey}`, async () => {
const result = await client.teams({ filter: { key: { eq: teamKey } } });
return result.nodes[0];
});
}
const CACHE_TTLS = {
teams: 600, // 10 min — teams almost never change
workflowStates: 1800, // 30 min — states rarely change
labels: 600, // 10 min — labels rarely change
issues: 60, // 1 min — issues change frequently
viewer: 3600, // 1 hr — your identity doesn't change
};
// Combined with webhook invalidation, even short TTLs
// dramatically reduce redundant requests
Skip irrelevant events to reduce processing costs.
async function processEvent(event: any): Promise<void> {
// Skip bot/automation events to avoid loops
if (event.actor?.type === "application") return;
// Skip trivial field updates (e.g., sortOrder changes)
if (event.type === "Issue" && event.action === "update") {
const significantFields = ["stateId", "assigneeId", "priority", "title"];
const changedFields = Object.keys(event.updatedFrom ?? {});
if (!changedFields.some(f => significantFields.includes(f))) return;
}
// Skip specific teams if not relevant
const relevantTeamKeys = ["ENG", "PRODUCT"];
if (event.data?.team?.key && !relevantTeamKeys.includes(event.data.team.key)) return;
// Process significant event
await handleEvent(event);
}
// Instead of fetching ALL issues every sync:
// Sort by updatedAt, stop when you reach already-synced data
async function incrementalSync(client: LinearClient, lastSyncTime: string) {
let cursor: string | undefined;
let synced = 0;
while (true) {
const issues = await client.issues({
first: 100,
after: cursor,
filter: { updatedAt: { gte: lastSyncTime } },
orderBy: "updatedAt",
});
for (const issue of issues.nodes) {
await upsertLocally(issue);
synced++;
}
if (!issues.pageInfo.hasNextPage) break;
cursor = issues.pageInfo.endCursor;
}
console.log(`Synced ${synced} issues since ${lastSyncTime}`);
return synced;
}
rawRequest() for exact field selectionupdatedAt for incremental sync| Error | Cause | Solution |
|---|---|---|
| Rate limit hit frequently | Too many requests | Implement coalescing + caching |
| Stale cache data | TTL too long | Use webhook-driven invalidation |
| High complexity queries | Nested relations | Flatten with rawRequest(), fetch relations lazily |
| Webhook processing overload | Unfiltered events | Add type/team/field filtering |
Structured design dialogue that validates ideas before implementation begins.
Comprehensive design intelligence for web and mobile UI/UX across 10 technology stacks.
Comprehensive implementation plans for multi-step tasks, breaking down specs into bite-sized, testable steps.
Introduction to the obra skills system with mandatory skill invocation rules and best practices.
Execute a written implementation plan with critical review and task checkpoints.
Delegate independent tasks to specialized agents working concurrently with isolated context.
Isolated git worktrees with smart directory selection and safety verification.
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Plan searchable and shareable content that drives traffic, builds authority, and generates leads.
README-first repository scanner that extracts commands and classifies reproduction candidates without executing them.
Brainstorm and prioritize marketing strategies tailored to your SaaS stage, budget, and goals.
Plan and optimize your website's page hierarchy, navigation, URL structure, and internal linking.
The world's fastest calendar for remote work
Capture, organize, and utilize your knowledge effortlessly.
Instant Summaries of Audio & Video Interviews with AnySummary
Transform PDFs into engaging mind maps.
Chat with any PDF instantly
Create an optimal daily plan using your voice
A productivity platform to centralize organizational knowledge and workflows with contextual AI assistance.
AskYourPDF Pricing Plans: Tailored to Your Needs