Repository navigation
Description
@bradygaster — PRD for your review.
Code Quality Linting for Squad Monorepo
Author: Book (Docs Agent)
Requested By: Dina Berry
Date: 2026-03-19
Status: Draft — Awaiting Brady's Review
Target Repository: bradygaster/squad
1. Problem Statement & Context
The Squad monorepo (bradygaster/squad) currently has no runtime behavior linter. The existing lint script runs tsc --noEmit, which provides type checking but cannot detect several classes of runtime anti-patterns:
What TypeScript strict mode misses:
- Synchronous I/O inside async functions — Blocking
fs.readFileSync()/writeFileSync()calls in async handlers, defeating the purpose of async declarations and blocking the Node.js event loop - Fire-and-forget promises — Missing
awaiton async calls, causing race conditions or silent errors - Unused code — Dead variables, imports, and parameters (
noUnusedLocals/noUnusedParametersare disabled) - Promise misuse — Conditionals testing promises instead of awaiting them
Real-world impact: Investigation of the SDK revealed 581 instances of sync file system calls inside async functions. For example, tools/index.ts contains squad_decide, squad_memory, and squad_skill handlers that are declared async but use readFileSync/writeFileSync. Every time an agent calls these tools, the event loop blocks. As the codebase grows and more contributors (including AI agents like Copilot) submit PRs, these patterns will compound without automated detection.
What we need: A lightweight, configurable linter that surfaces these issues during development without blocking the current workflow.
2. Goals & Success Metrics
Goals:
- Detect event loop blockers — Flag synchronous I/O inside async functions in hot paths (SDK tool handlers, platform utilities)
- Catch promise misuse — Identify missing
await, floating promises, and incorrect promise conditionals - Maintain velocity — No disruption to existing build/CI workflows; informational-only initially
- Enable incremental adoption — Team can triage findings and gradually increase enforcement
Success Metrics:
- Hot paths (SDK tool handlers) converted from sync to async I/O within 2 weeks of adoption
- Zero new sync-in-async patterns introduced after enforcement enabled
- Developer satisfaction: "Helpful findings, not noise" (measured via team feedback)
- CI remains green during Phase 1-2 rollout (no build blocking)
Non-goals:
- Replace TypeScript type checking
- Enforce opinionated code style preferences (e.g., semicolons, indentation)
- Block PRs immediately (phased enforcement only)
3. Key User Scenarios
Scenario 1: Developer submits PR with sync I/O in async handler
- Before: Pattern ships undetected, discovered later during performance testing
- After: Linter flags it in local development or CI logs; developer converts to
fs.promisesbefore merge
Scenario 2: AI agent (Copilot) generates code with floating promise
- Before: Missing
awaiton database save; PR reviewer spots it manually (or doesn't) - After: Linter flags missing
awaitin CI output; human reviewer confirms and fixes
Scenario 3: Maintainer evaluates linter findings
- Before: No visibility into async anti-patterns across codebase
- After: Run linter, review 1,058 findings, prioritize top 20 hot paths for immediate fix
Scenario 4: Team decides to enforce linting
- Before: Manual code review is the only gate
- After: Incrementally increase severity on critical rules (sync I/O, floating promises), CI enforces them
4. Scope and Non-Goals
In Scope:
- Detect async/promise anti-patterns (sync I/O, missing await, promise misuse)
- Flag type safety bypasses (
any, non-null assertions!) - Identify unused code (variables, imports)
- Informational warnings initially; phased enforcement later
- Integration with existing tooling (package.json scripts, GitHub Actions)
Out of Scope:
- Code formatting (use Prettier or similar if desired)
- Comprehensive security auditing (use dedicated security scanners)
- Migration of existing findings (team decides per-rule enforcement timeline)
- Linting non-TypeScript files (Markdown, JSON, etc.)
5. High-Level Approach: Solution Options
Three options exist, each with trade-offs around ecosystem maturity, speed, and detection capabilities:
Option 1: ESLint 9 with TypeScript Plugin
What it is: Industry-standard JavaScript/TypeScript linter with extensive plugin ecosystem. Uses TypeScript's type system for advanced analysis.
Pros:
- Type-aware rules — Can detect sync-in-async, floating promises, promise misuse (all require type information)
- Mature ecosystem — 10+ years, thousands of plugins, well-documented
- Flat config system — ESLint 9 uses modern
eslint.config.mjsformat (simpler than old.eslintrc) - Proven at scale — Used by Microsoft, Google, Airbnb, Meta
Cons:
- Heaviest option — 3 npm packages required (
eslint,@typescript-eslint/parser,@typescript-eslint/eslint-plugin, plus optionaleslint-plugin-nfor Node.js rules) - Slower — Type-aware analysis requires loading TypeScript compiler, runs slower than non-type-aware linters
- Steeper learning curve — Extensive configuration options (though flat config helps)
What it catches:
- ✅ Sync I/O in async functions (via
eslint-plugin-n) - ✅ Floating promises (
no-floating-promises) - ✅ Promise misuse (
no-misused-promises) - ✅ Unused vars,
any, non-null assertions
Proof of concept: Branch squad/eslint9-info-only implements this. Found 1,058 informational findings (581 sync-in-async, 17 floating promises, etc.). Technical spec available at .squad/skills/typescript-review/eslint9-spec.md.
Option 2: Biome
What it is: Newer Rust-based toolchain combining linting and formatting. Fast, opinionated, built-in TypeScript support.
Pros:
- Fast — Rust implementation, 10-100x faster than ESLint on large codebases
- All-in-one — Linter + formatter in one tool (like Prettier + ESLint combined)
- Built-in TypeScript — No separate parser plugin needed
- Simple config — Single
biome.jsonfile
Cons:
- No type-aware rules — Cannot detect sync-in-async or floating promises (requires type system integration, not yet available)
- Smaller rule set — ~200 rules vs ESLint's 1,000+ (though growing rapidly)
- Newer ecosystem — Less mature than ESLint, fewer community plugins
- Opinionated defaults — Less customizable than ESLint
What it catches:
- ❌ Cannot detect sync I/O in async (no Node.js plugin equivalent)
- ❌ Cannot detect floating promises (requires type awareness)
- ✅ Basic unused vars, suspicious patterns
- ✅ Some TypeScript anti-patterns (but not type-aware ones)
Verdict: Biome is excellent for speed and basic linting, but cannot address the core problem (sync-in-async detection).
Option 3: oxlint (Oxc Project)
What it is: Very fast Rust-based linter implementing subset of ESLint rules. Focused on speed over feature parity.
Pros:
- Fastest option — 50-100x faster than ESLint (Rust + optimized AST parsing)
- ESLint-compatible — Implements many ESLint rules, familiar syntax
- Single binary — No npm dependencies, just download and run
- Good for basics — Catches common mistakes quickly
Cons:
- No type-aware analysis — Cannot detect sync-in-async, floating promises, or promise misuse
- Subset of rules — Only ~200 ESLint rules implemented (vs 1,000+ total)
- No TypeScript-ESLint rules — Missing
@typescript-eslint/no-floating-promises, etc. - Limited configurability — Less customizable than ESLint
What it catches:
- ❌ Cannot detect sync I/O in async (no
eslint-plugin-nequivalent) - ❌ Cannot detect floating promises (no type awareness)
- ✅ Basic unused vars, syntax errors
- ✅ Some suspicious patterns (but limited rule set)
Verdict: oxlint is great for ultra-fast basic linting, but cannot address the core problem.
Recommendation Matrix
| Requirement | ESLint 9 | Biome | oxlint |
|---|---|---|---|
| Detects sync-in-async | ✅ Yes | ❌ No | ❌ No |
| Detects floating promises | ✅ Yes | ❌ No | ❌ No |
| Speed | Moderate | Fast | Fastest |
| Ecosystem maturity | Highest | Medium | Lowest |
| Configurability | Highest | Medium | Low |
| npm dependencies | 3 packages | 1 package | 0 (binary) |
If the priority is detecting sync-in-async and promise misuse (the stated problem), ESLint 9 is the only option that works. Biome and oxlint are excellent tools but cannot perform type-aware analysis needed to catch these patterns.
6. Risks, Dependencies, and Open Questions
Risks:
- Noisy output overwhelms developers — Mitigation: Start with warnings-only, separate script, opt-in usage
- Build time increase — Mitigation: ESLint 9 is fast enough for this small monorepo (~5 seconds); can run in parallel with
tsc - Team rejects after trying — Mitigation: Easy rollback (remove packages + config file)
- False positives in specific contexts — Mitigation: Test file exclusions already defined; can disable rules per-file if needed
Dependencies:
- Requires Node.js 18+ (already met)
- For Option 1: 3 npm packages (
eslint,@typescript-eslint/*,eslint-plugin-n) - For Option 2/3: Single package or binary
- No changes to existing CI/build scripts required for Phase 1
Open Questions for Brady:
- Which solution option? ESLint 9 (only option that solves sync-in-async), or evaluate Biome/oxlint for future when type-aware rules land?
- Triage strategy: Fix high-traffic paths (SDK tools) before enabling in CI, or surface all findings immediately?
- Test file linting: Fully exclude, use relaxed rules, or lint with same rules as source?
- Enforcement timeline: How long should warnings-only phase last before increasing severity to block builds?
- Rule customization: Any specific rules to enable/disable based on team preferences?
Next Steps:
- Brady selects solution option and answers open questions
- If ESLint 9: Merge proof-of-concept branch
squad/eslint9-info-only(ready now) - Team evaluates findings in local development (Phase 1: opt-in)
- Add to CI with
continue-on-error: true(Phase 2: visible but non-blocking) - Triage findings, tune rules, enable enforcement (Phase 3+)
Technical Specification Reference: For implementation details of Option 1 (ESLint 9), see .squad/skills/typescript-review/eslint9-technical-spec.md.
Proof of concept: Branch squad/eslint9-info-only off dev is ready with Option 1 (ESLint 9) implemented. 1,058 informational findings, exit code 0, nothing blocking.
Companion technical spec with full config, install commands, and adoption ladder is available on request.
cc @diberry
Activity
bradygaster commentedon Mar 22, 2026
🧪 Flight triage: Code quality linting PRD (ESLint 9, async/promise anti-patterns) is a monorepo quality initiative. Routing: FIDO (tests & quality). Ready for Brady's decision on solution option (ESLint 9 recommended for type-aware rules). PoC in squad/eslint9-info-only.
github-actions commentedon Mar 22, 2026
📋 Assigned to FIDO (Quality Owner)
Issue: #477 — PRD: Code Quality Linting for Squad Monorepo
FIDO will pick this up in the next Copilot session.
github-actions commentedon Mar 22, 2026
🏗️ Squad Triage — Flight (Lead)
Issue: #477 — PRD: Code Quality Linting for Squad Monorepo
Assigned to: FIDO (Quality Owner)
Reason: Issue relates to testing/quality work
@copilot evaluation: 🔴 Not suitable — issue involves work outside the coding agent's capability profile.
Team roster:
squad:flightsquad:proceduressquad:eecomsquad:fidosquad:paosquad:capcomsquad:controlsquad:surgeonsquad:boostersquad:gncsquad:networksquad:retrosquad:incosquad:guidosquad:telemetrysquad:voxsquad:dskysquad:simssquad:handbooksquad:ralphsquad:copilotfeat: add ESLint with TypeScript support for runtime anti-pattern det…
feat: add ESLint with TypeScript support for runtime anti-pattern det…
docs(ai-team): Cross-agent triage updates + history initialization