@jigyasudham/veto 0.8.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -52
- package/dist/agents/executor.js +36 -3
- package/dist/cli.js +350 -51
- package/dist/context/reader.js +113 -0
- package/dist/council/index.js +3 -1
- package/dist/plugins/loader.js +49 -0
- package/dist/router/index.js +2 -2
- package/dist/router/learning-updater.js +45 -1
- package/dist/server.js +478 -14
- package/dist/watcher/index.js +77 -0
- package/dist/workflow/pipeline.js +64 -0
- package/package.json +12 -3
- package/.claude/settings.local.json +0 -9
- package/src/adapters/claude.ts +0 -70
- package/src/adapters/codex.ts +0 -71
- package/src/adapters/gemini.ts +0 -71
- package/src/adapters/index.ts +0 -217
- package/src/agents/development/api.ts +0 -120
- package/src/agents/development/backend.ts +0 -85
- package/src/agents/development/coder.ts +0 -213
- package/src/agents/development/database.ts +0 -83
- package/src/agents/development/debugger.ts +0 -238
- package/src/agents/development/devops.ts +0 -86
- package/src/agents/development/frontend.ts +0 -85
- package/src/agents/development/migration.ts +0 -144
- package/src/agents/development/performance.ts +0 -144
- package/src/agents/development/refactor.ts +0 -86
- package/src/agents/development/reviewer.ts +0 -268
- package/src/agents/development/tester.ts +0 -151
- package/src/agents/executor.ts +0 -158
- package/src/agents/memory/context-manager.ts +0 -171
- package/src/agents/memory/decision-logger.ts +0 -160
- package/src/agents/memory/knowledge-base.ts +0 -124
- package/src/agents/memory/pattern-learner.ts +0 -143
- package/src/agents/memory/project-mapper.ts +0 -118
- package/src/agents/quality/accessibility.ts +0 -99
- package/src/agents/quality/code-quality.ts +0 -115
- package/src/agents/quality/compatibility.ts +0 -58
- package/src/agents/quality/documentation.ts +0 -105
- package/src/agents/quality/error-handling.ts +0 -96
- package/src/agents/research/competitor-analyzer.ts +0 -45
- package/src/agents/research/cost-analyzer.ts +0 -54
- package/src/agents/research/estimator.ts +0 -60
- package/src/agents/research/ethics-bias.ts +0 -113
- package/src/agents/research/researcher.ts +0 -114
- package/src/agents/research/risk-assessor.ts +0 -63
- package/src/agents/research/tech-advisor.ts +0 -55
- package/src/agents/security/auth.ts +0 -287
- package/src/agents/security/dependency-audit.ts +0 -337
- package/src/agents/security/penetration.ts +0 -262
- package/src/agents/security/privacy.ts +0 -285
- package/src/agents/security/scanner.ts +0 -322
- package/src/agents/security/secrets.ts +0 -249
- package/src/agents/types.ts +0 -66
- package/src/agents/workflow/automation.ts +0 -59
- package/src/agents/workflow/file-manager.ts +0 -52
- package/src/agents/workflow/git-agent.ts +0 -55
- package/src/agents/workflow/reporter.ts +0 -51
- package/src/agents/workflow/search-agent.ts +0 -40
- package/src/agents/workflow/task-coordinator.ts +0 -41
- package/src/agents/workflow/task-planner.ts +0 -47
- package/src/cli.ts +0 -135
- package/src/council/decision-engine.ts +0 -171
- package/src/council/devil-advocate.ts +0 -116
- package/src/council/index.ts +0 -44
- package/src/council/lead-developer.ts +0 -118
- package/src/council/legal-compliance.ts +0 -152
- package/src/council/product-manager.ts +0 -102
- package/src/council/security.ts +0 -172
- package/src/council/system-architect.ts +0 -132
- package/src/council/types.ts +0 -33
- package/src/council/ux-designer.ts +0 -121
- package/src/memory/local.ts +0 -305
- package/src/memory/schema.ts +0 -174
- package/src/memory/sync.ts +0 -274
- package/src/router/complexity-scorer.ts +0 -96
- package/src/router/context-compressor.ts +0 -74
- package/src/router/index.ts +0 -60
- package/src/router/learning-updater.ts +0 -271
- package/src/router/model-selector.ts +0 -83
- package/src/router/rate-monitor.ts +0 -103
- package/src/server.ts +0 -1038
- package/src/skills/development/skill-api-design.ts +0 -329
- package/src/skills/development/skill-auth.ts +0 -271
- package/src/skills/development/skill-ci-cd.ts +0 -0
- package/src/skills/development/skill-crud.ts +0 -209
- package/src/skills/development/skill-db-schema.ts +0 -0
- package/src/skills/development/skill-docker.ts +0 -0
- package/src/skills/development/skill-env-setup.ts +0 -0
- package/src/skills/development/skill-scaffold.ts +0 -323
- package/src/skills/intelligence/skill-complexity-score.ts +0 -69
- package/src/skills/intelligence/skill-cost-track.ts +0 -39
- package/src/skills/intelligence/skill-learning-loop.ts +0 -69
- package/src/skills/intelligence/skill-pattern-detect.ts +0 -38
- package/src/skills/intelligence/skill-rate-watch.ts +0 -61
- package/src/skills/memory/skill-context-compress.ts +0 -98
- package/src/skills/memory/skill-cross-sync.ts +0 -104
- package/src/skills/memory/skill-decision-log.ts +0 -119
- package/src/skills/memory/skill-session-restore.ts +0 -59
- package/src/skills/memory/skill-session-save.ts +0 -94
- package/src/skills/quality/skill-accessibility.ts +0 -0
- package/src/skills/quality/skill-code-review.ts +0 -84
- package/src/skills/quality/skill-docs-gen.ts +0 -0
- package/src/skills/quality/skill-perf-audit.ts +0 -0
- package/src/skills/quality/skill-security-scan.ts +0 -91
- package/src/skills/quality/skill-test-suite.ts +0 -290
- package/src/skills/workflow/skill-deploy.ts +0 -0
- package/src/skills/workflow/skill-git-workflow.ts +0 -0
- package/src/skills/workflow/skill-rollback.ts +0 -0
- package/src/skills/workflow/skill-task-breakdown.ts +0 -0
- package/tsconfig.json +0 -20
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { AgentPlan, WorkerAgentType } from '../types.js';
|
|
2
|
-
|
|
3
|
-
type PerfDomain = 'database' | 'network' | 'cpu' | 'memory' | 'frontend' | 'general';
|
|
4
|
-
|
|
5
|
-
function detectDomain(task: string, context?: string): PerfDomain {
|
|
6
|
-
const combined = (task + ' ' + (context ?? '')).toLowerCase();
|
|
7
|
-
if (combined.includes('database') || combined.includes('query') || combined.includes('sql') || combined.includes('slow query') || combined.includes('n+1')) return 'database';
|
|
8
|
-
if (combined.includes('network') || combined.includes('latency') || combined.includes('bandwidth') || combined.includes('api') || combined.includes('http')) return 'network';
|
|
9
|
-
if (combined.includes('cpu') || combined.includes('algorithm') || combined.includes('computation') || combined.includes('big-o') || combined.includes('loop')) return 'cpu';
|
|
10
|
-
if (combined.includes('memory') || combined.includes('ram') || combined.includes('heap') || combined.includes('leak') || combined.includes('gc')) return 'memory';
|
|
11
|
-
if (combined.includes('frontend') || combined.includes('render') || combined.includes('fcp') || combined.includes('lcp') || combined.includes('cls') || combined.includes('bundle')) return 'frontend';
|
|
12
|
-
return 'general';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const domainApproach: Record<PerfDomain, string> = {
|
|
16
|
-
database: 'Profile slow queries with EXPLAIN ANALYZE. Eliminate N+1 patterns. Add missing indexes. Implement query result caching. Consider read replicas for read-heavy workloads.',
|
|
17
|
-
network: 'Measure with real network traces (HAR file, curl --trace). Enable HTTP/2. Add connection pooling. Implement response compression. Use CDN for static assets. Cache at the edge.',
|
|
18
|
-
cpu: 'Profile with clinic.js or perf_hooks to find the hotspot. Check algorithm complexity — O(n²) over thousands of items is always the first target. Move heavy computation off the event loop (worker threads).',
|
|
19
|
-
memory: 'Take heap snapshots to identify retained objects. Implement LRU eviction on all caches. Remove event listeners on cleanup. Bound queue and buffer sizes. Configure GC flags for the workload.',
|
|
20
|
-
frontend: 'Measure Core Web Vitals (LCP, FID, CLS) with Lighthouse. Code-split by route. Lazy-load off-screen images. Tree-shake dependencies. Remove render-blocking scripts. Implement service worker caching.',
|
|
21
|
-
general: 'Establish a baseline measurement before optimising. Use a profiler to identify the actual bottleneck — never optimise by intuition. Fix one thing at a time and re-measure after each change.',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function plan(task: string, context?: string): AgentPlan {
|
|
25
|
-
const domain = detectDomain(task, context);
|
|
26
|
-
|
|
27
|
-
const domainSteps: Record<PerfDomain, string[]> = {
|
|
28
|
-
database: [
|
|
29
|
-
'Enable query logging and collect the 10 slowest queries over a representative time window',
|
|
30
|
-
'Run EXPLAIN ANALYZE on each slow query — read the execution plan top to bottom',
|
|
31
|
-
'Identify sequential scans on large tables — these are the highest-priority index candidates',
|
|
32
|
-
'Add indexes for missing JOIN columns, frequent WHERE filters, and ORDER BY columns',
|
|
33
|
-
'Detect N+1 queries by counting DB calls per HTTP request (use a query counter middleware)',
|
|
34
|
-
'Fix N+1 by using JOIN, eager loading (include in ORM), or DataLoader batching',
|
|
35
|
-
'Implement query result caching for expensive read-only queries (Redis, in-memory LRU)',
|
|
36
|
-
'Review ORM-generated SQL — ORMs frequently produce inefficient queries',
|
|
37
|
-
'Set statement_timeout to prevent runaway queries from blocking the connection pool',
|
|
38
|
-
'Consider read replicas for analytics queries that do not need fresh data',
|
|
39
|
-
'Benchmark before and after with a realistic data volume (not an empty dev database)',
|
|
40
|
-
],
|
|
41
|
-
network: [
|
|
42
|
-
'Capture a HAR file from the browser or use curl --trace for API calls',
|
|
43
|
-
'Measure Time to First Byte (TTFB) — high TTFB means server-side is slow',
|
|
44
|
-
'Enable HTTP/2 — multiplexes requests over a single connection',
|
|
45
|
-
'Enable Brotli or gzip compression on all text responses',
|
|
46
|
-
'Implement cache-control headers for static assets (immutable for hashed files)',
|
|
47
|
-
'Move static assets to a CDN geographically close to users',
|
|
48
|
-
'Reduce payload size — paginate, select only needed fields, avoid sending unused data',
|
|
49
|
-
'Implement connection pooling for HTTP clients making outbound requests',
|
|
50
|
-
'Add keep-alive headers to reuse TCP connections',
|
|
51
|
-
'Measure DNS lookup time — use DNS prefetching for known third-party domains',
|
|
52
|
-
'Benchmark with Apache Benchmark or k6 at realistic concurrency levels',
|
|
53
|
-
],
|
|
54
|
-
cpu: [
|
|
55
|
-
'Run clinic.js flame or Node.js --prof to identify the CPU hotspot',
|
|
56
|
-
'Analyse the flame graph — find the widest block (most self-time)',
|
|
57
|
-
'Check the algorithm complexity of the hotspot — is it O(n²) over a large input?',
|
|
58
|
-
'Replace O(n²) nested loops with O(n) approaches using hash maps or sorting',
|
|
59
|
-
'Move CPU-heavy synchronous work to a worker thread pool (piscina)',
|
|
60
|
-
'Cache the results of deterministic expensive computations (memoisation)',
|
|
61
|
-
'Avoid creating large temporary arrays in hot paths — reuse buffers',
|
|
62
|
-
'Use Buffer.allocUnsafe() instead of Buffer.alloc() where the content is immediately overwritten',
|
|
63
|
-
'Profile again after the fix — confirm the hotspot is gone, not just moved',
|
|
64
|
-
'Add a performance regression test that benchmarks the fixed function',
|
|
65
|
-
],
|
|
66
|
-
memory: [
|
|
67
|
-
'Take a heap snapshot with Chrome DevTools or --heapsnapshot-signal before the test',
|
|
68
|
-
'Run the operation that causes the memory growth',
|
|
69
|
-
'Take a second heap snapshot and use the Comparison view to find new retained objects',
|
|
70
|
-
'Identify the shortest GC root path to the retained objects',
|
|
71
|
-
'Find all unbounded caches and add LRU eviction (lru-cache npm package)',
|
|
72
|
-
'Audit all EventEmitter.on() calls — ensure off() is called in cleanup',
|
|
73
|
-
'Find all setInterval and setTimeout and ensure clearInterval/clearTimeout in cleanup',
|
|
74
|
-
'Check for closures that capture large objects unnecessarily',
|
|
75
|
-
'Set --max-old-space-size appropriately for the container memory limit',
|
|
76
|
-
'Monitor heap usage in production with process.memoryUsage() sent to metrics',
|
|
77
|
-
'Take a third heap snapshot after the fix to verify retention is resolved',
|
|
78
|
-
],
|
|
79
|
-
frontend: [
|
|
80
|
-
'Run Lighthouse on the production URL and record the Core Web Vitals baseline',
|
|
81
|
-
'Fix Largest Contentful Paint (LCP) — preload the hero image, avoid render-blocking CSS/JS',
|
|
82
|
-
'Fix Cumulative Layout Shift (CLS) — reserve space for images and ads with width/height attributes',
|
|
83
|
-
'Fix First Input Delay / Interaction to Next Paint — break up long tasks (> 50ms) on the main thread',
|
|
84
|
-
'Implement route-based code splitting with dynamic import()',
|
|
85
|
-
'Lazy-load images below the fold with loading="lazy" or IntersectionObserver',
|
|
86
|
-
'Audit the JavaScript bundle with webpack-bundle-analyzer or vite-bundle-visualizer',
|
|
87
|
-
'Remove unused dependencies — check import cost of each third-party library',
|
|
88
|
-
'Tree-shake lodash — import { debounce } from \'lodash\' not import _ from \'lodash\'',
|
|
89
|
-
'Implement service worker caching for repeat visits (Workbox)',
|
|
90
|
-
'Serve next-gen image formats (WebP, AVIF) with <picture> fallback',
|
|
91
|
-
'Measure again with Lighthouse after each change — confirm improvement',
|
|
92
|
-
],
|
|
93
|
-
general: [
|
|
94
|
-
'Establish a baseline — measure latency (p50, p95, p99), throughput (rps), CPU, and memory',
|
|
95
|
-
'Use a profiler appropriate to the stack — clinic.js, Pyspy, JProfiler, pprof',
|
|
96
|
-
'Identify the single largest bottleneck — fix that first before anything else',
|
|
97
|
-
'Check for database query issues (N+1, missing indexes, full table scans)',
|
|
98
|
-
'Check for algorithmic complexity issues (O(n²) loops)',
|
|
99
|
-
'Check for I/O blocking the event loop (synchronous file/network reads)',
|
|
100
|
-
'Check for memory pressure causing GC pauses',
|
|
101
|
-
'Fix one bottleneck at a time and re-measure after each change',
|
|
102
|
-
'Add performance regression tests to CI (k6, autocannon, benchmark.js)',
|
|
103
|
-
'Document the improvement with before/after numbers',
|
|
104
|
-
],
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
agent: 'performance' as WorkerAgentType,
|
|
109
|
-
task,
|
|
110
|
-
tier: 3,
|
|
111
|
-
approach: domainApproach[domain],
|
|
112
|
-
steps: domainSteps[domain],
|
|
113
|
-
checklist: [
|
|
114
|
-
'[ ] Baseline measurements recorded (p50/p95/p99 latency, throughput, CPU, memory)',
|
|
115
|
-
'[ ] Profiler run — bottleneck identified, not assumed',
|
|
116
|
-
'[ ] Highest-impact bottleneck fixed first',
|
|
117
|
-
'[ ] No N+1 query patterns in the hot path',
|
|
118
|
-
'[ ] All caches have a maximum size and eviction policy',
|
|
119
|
-
'[ ] No synchronous blocking I/O in async code paths',
|
|
120
|
-
'[ ] Algorithm complexity reviewed — no O(n²) over large inputs',
|
|
121
|
-
'[ ] Post-fix measurement confirms improvement',
|
|
122
|
-
'[ ] Performance regression test added to CI',
|
|
123
|
-
'[ ] Improvement documented with before/after numbers',
|
|
124
|
-
],
|
|
125
|
-
pitfalls: [
|
|
126
|
-
'Optimising before profiling — wastes hours on non-bottlenecks',
|
|
127
|
-
'Caching without an eviction strategy — trades a performance problem for a memory leak',
|
|
128
|
-
'Fixing the wrong layer — optimising the API handler when the bottleneck is the database',
|
|
129
|
-
'Measuring on a development machine with a tiny dataset — results do not reflect production',
|
|
130
|
-
'Parallelising I/O without a concurrency limit — overwhelms the downstream service and causes cascading failure',
|
|
131
|
-
'Trusting microbenchmarks for macro performance — benchmark the real workload, not a toy example',
|
|
132
|
-
],
|
|
133
|
-
patterns: [
|
|
134
|
-
'Memoisation (cache pure function results)',
|
|
135
|
-
'Lazy evaluation (compute on demand, not upfront)',
|
|
136
|
-
'Connection pooling (reuse expensive connections)',
|
|
137
|
-
'Read-through cache (populate cache on miss, serve from cache on hit)',
|
|
138
|
-
'Worker thread pool (offload CPU work from the event loop)',
|
|
139
|
-
'Pagination and cursor-based pagination (bound response size)',
|
|
140
|
-
'Index-only queries (cover all needed columns in the index)',
|
|
141
|
-
],
|
|
142
|
-
duration_estimate: '4-8 hours',
|
|
143
|
-
};
|
|
144
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { AgentPlan, WorkerAgentType } from '../types.js';
|
|
2
|
-
|
|
3
|
-
type RefactorFocus = 'complexity' | 'duplication' | 'naming' | 'architecture' | 'types' | 'general';
|
|
4
|
-
|
|
5
|
-
function detectFocus(task: string, context?: string): RefactorFocus {
|
|
6
|
-
const combined = (task + ' ' + (context ?? '')).toLowerCase();
|
|
7
|
-
if (combined.includes('complex') || combined.includes('nesting') || combined.includes('long function') || combined.includes('spaghetti')) return 'complexity';
|
|
8
|
-
if (combined.includes('duplicat') || combined.includes('copy') || combined.includes('repeat') || combined.includes('dry')) return 'duplication';
|
|
9
|
-
if (combined.includes('naming') || combined.includes('rename') || combined.includes('readab')) return 'naming';
|
|
10
|
-
if (combined.includes('architect') || combined.includes('layer') || combined.includes('module') || combined.includes('structure')) return 'architecture';
|
|
11
|
-
if (combined.includes('type') || combined.includes('any') || combined.includes('typescript') || combined.includes('interface')) return 'types';
|
|
12
|
-
return 'general';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const focusApproach: Record<RefactorFocus, string> = {
|
|
16
|
-
complexity: 'Identify the most complex functions first using cyclomatic complexity metrics. Extract sub-functions, apply guard clauses, replace conditionals with polymorphism, reduce nesting step by step.',
|
|
17
|
-
duplication: 'Find all duplicated logic using diff tools or manual inspection. Extract to a shared utility/hook/module. Parameterise differences. Verify all callers still work after consolidation.',
|
|
18
|
-
naming: 'Rename systematically using IDE refactoring tools, not text-replace. Start with the most confusing names. Add JSDoc where a name alone is insufficient. Update all references atomically.',
|
|
19
|
-
architecture: 'Diagram the current structure, identify coupling hotspots, define the target architecture, then migrate incrementally with an anti-corruption layer during transition.',
|
|
20
|
-
types: 'Eliminate all implicit any types by enabling TypeScript strict mode. Define precise interfaces for all data shapes. Replace type assertions with proper narrowing. Add runtime validation at boundaries.',
|
|
21
|
-
general: 'Apply the Boy Scout Rule: leave the code cleaner than you found it. Identify the highest-impact improvement (complexity, duplication, naming, or architecture) and address it first.',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function plan(task: string, context?: string): AgentPlan {
|
|
25
|
-
const focus = detectFocus(task, context);
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
agent: 'refactor' as WorkerAgentType,
|
|
29
|
-
task,
|
|
30
|
-
tier: 2,
|
|
31
|
-
approach: focusApproach[focus],
|
|
32
|
-
steps: [
|
|
33
|
-
'Run the full test suite and record the baseline — all tests must pass before and after',
|
|
34
|
-
'Identify code smells: long functions, deep nesting, duplicated logic, primitive obsession, feature envy',
|
|
35
|
-
'Prioritise by impact — fix the highest-complexity or most-duplicated code first',
|
|
36
|
-
'Add missing tests for code that lacks coverage before refactoring it',
|
|
37
|
-
'Apply extract function refactoring to any function longer than 30 lines',
|
|
38
|
-
'Apply guard clauses (early returns) to reduce nesting depth',
|
|
39
|
-
'Extract magic numbers and strings to named constants',
|
|
40
|
-
'Rename variables and functions to be self-documenting — eliminate abbreviations',
|
|
41
|
-
'Consolidate duplicated logic into shared utilities or base classes',
|
|
42
|
-
'Remove dead code — if it is not tested and not called, delete it',
|
|
43
|
-
'Apply TypeScript strict improvements — eliminate any, add precise types',
|
|
44
|
-
'Run the linter and formatter after each logical change',
|
|
45
|
-
'Run the full test suite again — compare with baseline, all tests must pass',
|
|
46
|
-
'Review the diff — if a single commit is too large, split into atomic changes',
|
|
47
|
-
],
|
|
48
|
-
checklist: [
|
|
49
|
-
'[ ] All tests pass before starting refactoring',
|
|
50
|
-
'[ ] All tests still pass after each refactoring step',
|
|
51
|
-
'[ ] No function exceeds 30 lines of executable code after refactoring',
|
|
52
|
-
'[ ] Nesting depth reduced to maximum 3 levels',
|
|
53
|
-
'[ ] All magic numbers replaced with named constants',
|
|
54
|
-
'[ ] No duplicated code blocks (copy-paste > 5 lines)',
|
|
55
|
-
'[ ] All variables and functions have descriptive names',
|
|
56
|
-
'[ ] No implicit any types remain',
|
|
57
|
-
'[ ] Dead code removed (unused functions, imports, variables)',
|
|
58
|
-
'[ ] Linter passes with zero warnings',
|
|
59
|
-
'[ ] Code coverage not reduced by the refactoring',
|
|
60
|
-
'[ ] Each commit is a single atomic refactoring step',
|
|
61
|
-
'[ ] Public API is unchanged (or version-bumped if breaking)',
|
|
62
|
-
'[ ] Complexity metrics improved — measure before and after',
|
|
63
|
-
],
|
|
64
|
-
pitfalls: [
|
|
65
|
-
'Refactoring without tests — you will not know if you broke something',
|
|
66
|
-
'Big-bang refactoring in one commit — impossible to review, high merge conflict risk',
|
|
67
|
-
'Renaming without IDE refactoring tools — misses call sites, creates runtime bugs',
|
|
68
|
-
'Extracting a function that is only called once without a clear abstraction reason',
|
|
69
|
-
'Changing behaviour while refactoring — refactoring must be semantics-preserving',
|
|
70
|
-
'Removing code that looks unused but is called via dynamic reflection or eval',
|
|
71
|
-
'Over-abstracting — a third function extracted for two call sites adds indirection without clarity',
|
|
72
|
-
],
|
|
73
|
-
patterns: [
|
|
74
|
-
'Extract Function (Fowler refactoring catalogue)',
|
|
75
|
-
'Replace Conditional with Polymorphism',
|
|
76
|
-
'Guard Clause (replace nested conditionals with early returns)',
|
|
77
|
-
'Introduce Parameter Object (replace long parameter lists)',
|
|
78
|
-
'Replace Magic Number with Symbolic Constant',
|
|
79
|
-
'Move Function (reduce feature envy)',
|
|
80
|
-
'Inline Variable (remove unnecessary variable aliases)',
|
|
81
|
-
'Decompose Conditional',
|
|
82
|
-
'Consolidate Duplicate Conditional Fragments',
|
|
83
|
-
],
|
|
84
|
-
duration_estimate: focus === 'architecture' ? '1-3 days' : '2-6 hours',
|
|
85
|
-
};
|
|
86
|
-
}
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { AgentPlan, AgentAnalysis, AgentFinding, FindingSeverity, WorkerAgentType } from '../types.js';
|
|
2
|
-
|
|
3
|
-
// ─── plan ────────────────────────────────────────────────────────────────────
|
|
4
|
-
|
|
5
|
-
export function plan(task: string, _context?: string): AgentPlan {
|
|
6
|
-
return {
|
|
7
|
-
agent: 'reviewer' as WorkerAgentType,
|
|
8
|
-
task,
|
|
9
|
-
tier: 2,
|
|
10
|
-
approach: 'Systematic code review: assess complexity, error handling, naming, magic literals, nesting depth, and test coverage. Produce scored findings with actionable fixes.',
|
|
11
|
-
steps: [
|
|
12
|
-
'Read the module top-to-bottom to understand its purpose and public contract',
|
|
13
|
-
'Identify all exported symbols and verify they are intentional',
|
|
14
|
-
'Check function lengths — flag any function exceeding 50 lines for extraction',
|
|
15
|
-
'Assess cyclomatic complexity — any function with more than 10 branches is a risk',
|
|
16
|
-
'Verify every async call is awaited or explicitly fire-and-forget',
|
|
17
|
-
'Check error handling — all try/catch blocks must do something meaningful',
|
|
18
|
-
'Scan for magic numbers and untyped string literals',
|
|
19
|
-
'Measure nesting depth — refactor anything deeper than 3 levels',
|
|
20
|
-
'Review naming — variables, functions, and types should be self-documenting',
|
|
21
|
-
'Check for TODO/FIXME comments and ensure each links to a tracking issue',
|
|
22
|
-
'Verify unused imports and variables are removed',
|
|
23
|
-
'Confirm test coverage exists for every public function',
|
|
24
|
-
'Assess consistency with the rest of the codebase style',
|
|
25
|
-
],
|
|
26
|
-
checklist: [
|
|
27
|
-
'[ ] No function exceeds 50 lines of executable code',
|
|
28
|
-
'[ ] No function has cyclomatic complexity > 10',
|
|
29
|
-
'[ ] Every await is inside an async function',
|
|
30
|
-
'[ ] Every try/catch logs or rethrows — no silent swallow',
|
|
31
|
-
'[ ] No magic numbers — use named constants',
|
|
32
|
-
'[ ] No deeply nested if/for (max 3 levels)',
|
|
33
|
-
'[ ] All TODO/FIXME comments reference a ticket number',
|
|
34
|
-
'[ ] No unused variables or imports',
|
|
35
|
-
'[ ] Naming is consistent with the project convention (camelCase, PascalCase for types)',
|
|
36
|
-
'[ ] No console.log left in production paths',
|
|
37
|
-
'[ ] Public functions have JSDoc',
|
|
38
|
-
'[ ] Every exported function has at least one test',
|
|
39
|
-
'[ ] No any types without an explanatory comment',
|
|
40
|
-
'[ ] No circular imports',
|
|
41
|
-
],
|
|
42
|
-
pitfalls: [
|
|
43
|
-
'Approving a PR because it "looks fine" without running it locally',
|
|
44
|
-
'Missing async bugs — a synchronous function calling an async one without await silently drops the Promise',
|
|
45
|
-
'Over-reviewing style while missing logic errors — focus on correctness first',
|
|
46
|
-
'Not reading the tests — tests reveal intent, bad tests reveal bad design',
|
|
47
|
-
'Ignoring error handling because the happy path looks clean',
|
|
48
|
-
],
|
|
49
|
-
patterns: [
|
|
50
|
-
'Guard clause pattern (early returns to reduce nesting)',
|
|
51
|
-
'Strategy pattern (replace long switch/if chains)',
|
|
52
|
-
'Extract function refactoring',
|
|
53
|
-
'Null Object pattern (replace null checks)',
|
|
54
|
-
'Result/Option type (replace throw-based error handling)',
|
|
55
|
-
],
|
|
56
|
-
duration_estimate: '1-2 hours',
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
function countLines(code: string): number {
|
|
63
|
-
return code.split('\n').length;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function detectFunctions(code: string): Array<{ name: string; body: string; start: number }> {
|
|
67
|
-
const results: Array<{ name: string; body: string; start: number }> = [];
|
|
68
|
-
const lines = code.split('\n');
|
|
69
|
-
|
|
70
|
-
// Match function declarations and arrow functions assigned to const/let/var
|
|
71
|
-
const funcRegex = /(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\()/;
|
|
72
|
-
|
|
73
|
-
let i = 0;
|
|
74
|
-
while (i < lines.length) {
|
|
75
|
-
const match = lines[i].match(funcRegex);
|
|
76
|
-
if (match) {
|
|
77
|
-
const name = match[1] || match[2] || 'anonymous';
|
|
78
|
-
let depth = 0;
|
|
79
|
-
let started = false;
|
|
80
|
-
const bodyLines: string[] = [];
|
|
81
|
-
let j = i;
|
|
82
|
-
while (j < lines.length) {
|
|
83
|
-
for (const ch of lines[j]) {
|
|
84
|
-
if (ch === '{') { depth++; started = true; }
|
|
85
|
-
if (ch === '}') depth--;
|
|
86
|
-
}
|
|
87
|
-
bodyLines.push(lines[j]);
|
|
88
|
-
if (started && depth === 0) break;
|
|
89
|
-
j++;
|
|
90
|
-
}
|
|
91
|
-
results.push({ name, body: bodyLines.join('\n'), start: i + 1 });
|
|
92
|
-
i = j + 1;
|
|
93
|
-
} else {
|
|
94
|
-
i++;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return results;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function maxNestingDepth(code: string): number {
|
|
101
|
-
let max = 0;
|
|
102
|
-
let depth = 0;
|
|
103
|
-
for (const ch of code) {
|
|
104
|
-
if (ch === '{') { depth++; max = Math.max(max, depth); }
|
|
105
|
-
if (ch === '}') depth--;
|
|
106
|
-
}
|
|
107
|
-
return max;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ─── analyze ─────────────────────────────────────────────────────────────────
|
|
111
|
-
|
|
112
|
-
export function analyze(code: string, context?: string): AgentAnalysis {
|
|
113
|
-
const findings: AgentFinding[] = [];
|
|
114
|
-
const lines = code.split('\n');
|
|
115
|
-
const totalLines = countLines(code);
|
|
116
|
-
const subject = context ?? `Code (${totalLines} lines)`;
|
|
117
|
-
|
|
118
|
-
// 1. Long functions
|
|
119
|
-
const functions = detectFunctions(code);
|
|
120
|
-
for (const fn of functions) {
|
|
121
|
-
const fnLines = fn.body.split('\n').length;
|
|
122
|
-
if (fnLines > 50) {
|
|
123
|
-
findings.push({
|
|
124
|
-
severity: 'medium',
|
|
125
|
-
category: 'Complexity',
|
|
126
|
-
description: `Function '${fn.name}' is ${fnLines} lines long (limit: 50).`,
|
|
127
|
-
fix: `Extract logical blocks into named helper functions. Aim for functions that do one thing and fit on one screen.`,
|
|
128
|
-
location: `Line ${fn.start}`,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// 2. Deep nesting
|
|
134
|
-
const nestingDepth = maxNestingDepth(code);
|
|
135
|
-
if (nestingDepth > 5) {
|
|
136
|
-
findings.push({
|
|
137
|
-
severity: 'medium',
|
|
138
|
-
category: 'Complexity',
|
|
139
|
-
description: `Maximum brace nesting depth is ${nestingDepth} (limit: 5). Deep nesting makes code hard to reason about.`,
|
|
140
|
-
fix: 'Apply guard clauses (early returns), extract inner blocks to functions, or flatten with Promise chains.',
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 3. Missing error handling — try without catch, or bare promise .then without .catch
|
|
145
|
-
const tryCatchBalance = (code.match(/\btry\b/g) || []).length;
|
|
146
|
-
const catchBalance = (code.match(/\bcatch\b/g) || []).length;
|
|
147
|
-
if (tryCatchBalance > catchBalance) {
|
|
148
|
-
findings.push({
|
|
149
|
-
severity: 'high',
|
|
150
|
-
category: 'Error Handling',
|
|
151
|
-
description: `Found ${tryCatchBalance} try block(s) but only ${catchBalance} catch block(s). Unmatched try blocks may silently swallow errors.`,
|
|
152
|
-
fix: 'Ensure every try has a corresponding catch. Log the error and either rethrow or return a typed error result.',
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// 4. Unhandled promise (.then without .catch)
|
|
157
|
-
lines.forEach((line, idx) => {
|
|
158
|
-
if (/\.then\s*\(/.test(line) && !/\.catch\s*\(/.test(line)) {
|
|
159
|
-
// Check next few lines for .catch
|
|
160
|
-
const window = lines.slice(idx, idx + 5).join(' ');
|
|
161
|
-
if (!/\.catch\s*\(/.test(window)) {
|
|
162
|
-
findings.push({
|
|
163
|
-
severity: 'high',
|
|
164
|
-
category: 'Error Handling',
|
|
165
|
-
description: `Promise .then() at line ${idx + 1} has no visible .catch() — unhandled rejection risk.`,
|
|
166
|
-
fix: 'Add .catch(err => { ... }) or convert to async/await with try/catch.',
|
|
167
|
-
location: `Line ${idx + 1}`,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// 5. Magic numbers
|
|
174
|
-
lines.forEach((line, idx) => {
|
|
175
|
-
// Skip comments and import lines
|
|
176
|
-
if (/^\s*(\/\/|\/\*|\*|import|export)/.test(line)) return;
|
|
177
|
-
// Match standalone numeric literals that aren't 0 or 1
|
|
178
|
-
const magicMatches = line.match(/(?<![.\w])(?:[2-9]\d*|\d{2,})(?!\w)/g);
|
|
179
|
-
if (magicMatches) {
|
|
180
|
-
findings.push({
|
|
181
|
-
severity: 'low',
|
|
182
|
-
category: 'Maintainability',
|
|
183
|
-
description: `Magic number(s) [${magicMatches.join(', ')}] at line ${idx + 1}. Bare literals obscure intent.`,
|
|
184
|
-
fix: 'Extract to a named constant: const MAX_RETRIES = 3; at the top of the file or in a constants module.',
|
|
185
|
-
location: `Line ${idx + 1}`,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// 6. TODO/FIXME without issue reference
|
|
191
|
-
lines.forEach((line, idx) => {
|
|
192
|
-
if (/\/\/.*\b(TODO|FIXME|HACK|XXX)\b/.test(line) && !/\b(#\d+|https?:\/\/|JIRA|GH-|issue)/i.test(line)) {
|
|
193
|
-
findings.push({
|
|
194
|
-
severity: 'low',
|
|
195
|
-
category: 'Technical Debt',
|
|
196
|
-
description: `Untracked TODO/FIXME at line ${idx + 1}: "${line.trim()}"`,
|
|
197
|
-
fix: 'Link to a tracking issue: // TODO(#123): fix this.',
|
|
198
|
-
location: `Line ${idx + 1}`,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// 7. Unused variable patterns (_var that is actually used, or var that starts with _ but is referenced)
|
|
204
|
-
lines.forEach((line, idx) => {
|
|
205
|
-
if (/const\s+_\w+\s*=/.test(line) || /let\s+_\w+\s*=/.test(line)) {
|
|
206
|
-
const varMatch = line.match(/(?:const|let)\s+(_\w+)/);
|
|
207
|
-
if (varMatch) {
|
|
208
|
-
const varName = varMatch[1];
|
|
209
|
-
const usedElsewhere = code.split('\n').filter((_, i) => i !== idx).some(l => l.includes(varName));
|
|
210
|
-
if (usedElsewhere) {
|
|
211
|
-
findings.push({
|
|
212
|
-
severity: 'info',
|
|
213
|
-
category: 'Naming',
|
|
214
|
-
description: `Variable '${varName}' starts with underscore (convention: unused) but is referenced elsewhere in the code.`,
|
|
215
|
-
fix: `Rename '${varName}' to '${varName.slice(1)}' since it is actively used.`,
|
|
216
|
-
location: `Line ${idx + 1}`,
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// 8. Consistent naming — detect mixed camelCase / snake_case for variables
|
|
224
|
-
const camelVars = (code.match(/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*)\b/g) || []).length;
|
|
225
|
-
const snakeVars = (code.match(/\b(?:const|let|var)\s+([a-z][a-z0-9]*_[a-z][a-z0-9]*)\b/g) || []).length;
|
|
226
|
-
if (camelVars > 0 && snakeVars > 0) {
|
|
227
|
-
findings.push({
|
|
228
|
-
severity: 'low',
|
|
229
|
-
category: 'Naming',
|
|
230
|
-
description: `Mixed naming convention: ${camelVars} camelCase and ${snakeVars} snake_case variable declarations detected.`,
|
|
231
|
-
fix: 'Standardise on camelCase for variables and functions in TypeScript/JavaScript codebases.',
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ─── Score ────────────────────────────────────────────────────────────────
|
|
236
|
-
let score = 100;
|
|
237
|
-
const critical_count = findings.filter(f => f.severity === 'critical').length;
|
|
238
|
-
const high_count = findings.filter(f => f.severity === 'high').length;
|
|
239
|
-
const medium_count = findings.filter(f => f.severity === 'medium').length;
|
|
240
|
-
const low_count = findings.filter(f => f.severity === 'low').length;
|
|
241
|
-
|
|
242
|
-
score -= critical_count * 25;
|
|
243
|
-
score -= high_count * 15;
|
|
244
|
-
score -= medium_count * 8;
|
|
245
|
-
score -= low_count * 3;
|
|
246
|
-
score = Math.max(0, Math.min(100, score));
|
|
247
|
-
|
|
248
|
-
let verdict: AgentAnalysis['verdict'];
|
|
249
|
-
if (critical_count > 0) verdict = 'rejected';
|
|
250
|
-
else if (high_count > 0) verdict = 'needs_revision';
|
|
251
|
-
else if (medium_count > 0 || low_count > 2) verdict = 'approved_with_warnings';
|
|
252
|
-
else verdict = 'approved';
|
|
253
|
-
|
|
254
|
-
const summary = findings.length === 0
|
|
255
|
-
? 'Code looks clean — no significant issues detected.'
|
|
256
|
-
: `Found ${findings.length} issue(s): ${critical_count} critical, ${high_count} high, ${medium_count} medium, ${low_count} low. Score: ${score}/100.`;
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
agent: 'reviewer' as WorkerAgentType,
|
|
260
|
-
subject,
|
|
261
|
-
findings,
|
|
262
|
-
score,
|
|
263
|
-
verdict,
|
|
264
|
-
summary,
|
|
265
|
-
critical_count,
|
|
266
|
-
high_count,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { AgentPlan, WorkerAgentType } from '../types.js';
|
|
2
|
-
|
|
3
|
-
type TestType = 'unit' | 'integration' | 'e2e';
|
|
4
|
-
|
|
5
|
-
function detectTestType(task: string, context?: string): TestType {
|
|
6
|
-
const combined = (task + ' ' + (context ?? '')).toLowerCase();
|
|
7
|
-
if (combined.includes('e2e') || combined.includes('end-to-end') || combined.includes('playwright') || combined.includes('cypress') || combined.includes('browser')) return 'e2e';
|
|
8
|
-
if (combined.includes('integration') || combined.includes('database') || combined.includes('api') || combined.includes('endpoint') || combined.includes('http')) return 'integration';
|
|
9
|
-
return 'unit';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const typeApproach: Record<TestType, string> = {
|
|
13
|
-
unit: 'Isolate the unit under test by mocking all dependencies. Cover happy path, error paths, boundary conditions, and type edge cases. Aim for 100% branch coverage on business logic.',
|
|
14
|
-
integration: 'Test the interaction between real components (DB, HTTP, filesystem). Use test containers or in-memory alternatives. Verify contracts and side effects, not just return values.',
|
|
15
|
-
e2e: 'Test user journeys from browser to backend. Run against a staging environment or local stack. Focus on critical paths — registration, login, core workflows. Avoid testing every permutation.',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const typeSteps: Record<TestType, string[]> = {
|
|
19
|
-
unit: [
|
|
20
|
-
'Identify the public interface of the unit under test',
|
|
21
|
-
'List all logical branches (if/else, switch, try/catch, null checks)',
|
|
22
|
-
'Create a test file co-located with the source file (*.test.ts)',
|
|
23
|
-
'Import the unit and mock all external dependencies at the top',
|
|
24
|
-
'Write the happy-path test first to establish baseline behaviour',
|
|
25
|
-
'Write a test for each error or exception path',
|
|
26
|
-
'Add boundary condition tests (empty array, zero, max value, null, undefined)',
|
|
27
|
-
'Add a test for each async path (resolved and rejected)',
|
|
28
|
-
'Verify mocks were called with the correct arguments',
|
|
29
|
-
'Run coverage report and fill gaps — target 90%+ branch coverage',
|
|
30
|
-
'Review test names — each should describe behaviour, not implementation',
|
|
31
|
-
'Run tests in watch mode and confirm they are deterministic across runs',
|
|
32
|
-
],
|
|
33
|
-
integration: [
|
|
34
|
-
'Identify the integration boundary (DB, HTTP, message queue, filesystem)',
|
|
35
|
-
'Set up a test database or mock server that resets between tests',
|
|
36
|
-
'Write fixtures / seed data helpers',
|
|
37
|
-
'Test the full request-response cycle for each endpoint/operation',
|
|
38
|
-
'Verify response body schema and HTTP status codes',
|
|
39
|
-
'Verify database state after mutations',
|
|
40
|
-
'Test authentication and authorisation (valid token, expired token, wrong role)',
|
|
41
|
-
'Test concurrent requests do not corrupt shared state',
|
|
42
|
-
'Test rollback on partial failure (transaction integrity)',
|
|
43
|
-
'Clean up all test data in afterEach/afterAll hooks',
|
|
44
|
-
'Run tests against the CI database, not a developer\'s local DB',
|
|
45
|
-
'Assert on error response shape, not just status code',
|
|
46
|
-
],
|
|
47
|
-
e2e: [
|
|
48
|
-
'Map the user journeys to test (registration, login, core workflow)',
|
|
49
|
-
'Set up a local or staging stack with predictable seed data',
|
|
50
|
-
'Write page object models (POMs) for each major screen',
|
|
51
|
-
'Implement the happy-path journey test first',
|
|
52
|
-
'Add negative journey tests (invalid login, blocked access)',
|
|
53
|
-
'Test form validation messages appear and are readable',
|
|
54
|
-
'Verify redirect behaviour after actions (post-login, post-submit)',
|
|
55
|
-
'Test on at least two viewport sizes (mobile and desktop)',
|
|
56
|
-
'Add accessibility checks (axe-core or Playwright a11y plugin)',
|
|
57
|
-
'Assert on URL changes to ensure navigation works',
|
|
58
|
-
'Run in headless mode in CI; headed mode for debugging',
|
|
59
|
-
'Keep each test independent — never rely on state from a previous test',
|
|
60
|
-
],
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const typeChecklist: Record<TestType, string[]> = {
|
|
64
|
-
unit: [
|
|
65
|
-
'[ ] Every public function has at least one test',
|
|
66
|
-
'[ ] Every branch of a conditional is exercised',
|
|
67
|
-
'[ ] Error throw / rejection is tested explicitly',
|
|
68
|
-
'[ ] Mocks are reset between tests (beforeEach)',
|
|
69
|
-
'[ ] No test depends on another test\'s side effects',
|
|
70
|
-
'[ ] Test names use "should <behaviour> when <condition>" format',
|
|
71
|
-
'[ ] No production code altered to make tests pass (unless it improves design)',
|
|
72
|
-
'[ ] Tests run in under 5 seconds total',
|
|
73
|
-
'[ ] Coverage threshold enforced in jest/vitest config',
|
|
74
|
-
'[ ] Snapshot tests (if used) are committed and reviewed',
|
|
75
|
-
],
|
|
76
|
-
integration: [
|
|
77
|
-
'[ ] Test database is isolated from development database',
|
|
78
|
-
'[ ] Seed data is deterministic and version-controlled',
|
|
79
|
-
'[ ] All test data cleaned up after each test',
|
|
80
|
-
'[ ] Auth tokens generated fresh per test',
|
|
81
|
-
'[ ] Response schema validated against TypeScript types',
|
|
82
|
-
'[ ] Error response shape tested, not just status code',
|
|
83
|
-
'[ ] Transactions roll back correctly on failure',
|
|
84
|
-
'[ ] No hardcoded ports — use dynamic port allocation',
|
|
85
|
-
'[ ] CI pipeline runs integration tests in a Docker environment',
|
|
86
|
-
'[ ] Tests are tagged so unit and integration can run separately',
|
|
87
|
-
],
|
|
88
|
-
e2e: [
|
|
89
|
-
'[ ] Page object models used — no raw selectors in test bodies',
|
|
90
|
-
'[ ] data-testid attributes used for element selection (not CSS classes)',
|
|
91
|
-
'[ ] Each test is fully independent (own seed data)',
|
|
92
|
-
'[ ] Tests run in headless mode in CI',
|
|
93
|
-
'[ ] Mobile and desktop viewports both tested',
|
|
94
|
-
'[ ] Accessibility check passes on each major screen',
|
|
95
|
-
'[ ] Network errors are simulated for critical flows',
|
|
96
|
-
'[ ] Screenshots taken on failure for debugging',
|
|
97
|
-
'[ ] Test retries configured (max 2) to handle flakiness',
|
|
98
|
-
'[ ] Critical path coverage maps to product requirements',
|
|
99
|
-
],
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const typePitfalls: Record<TestType, string[]> = {
|
|
103
|
-
unit: [
|
|
104
|
-
'Testing implementation details (private methods, internal state) — test behaviour, not internals',
|
|
105
|
-
'Mocking the module under test — only mock its dependencies',
|
|
106
|
-
'Asserting on mock call count without asserting on arguments',
|
|
107
|
-
'Writing tests after the fact without TDD discipline — causes incomplete coverage',
|
|
108
|
-
'Using Math.random() or Date.now() in tests without mocking — non-deterministic results',
|
|
109
|
-
],
|
|
110
|
-
integration: [
|
|
111
|
-
'Running integration tests against shared databases — causes test pollution between developers',
|
|
112
|
-
'Forgetting to rollback DB transactions — leaves dirty state that breaks subsequent tests',
|
|
113
|
-
'Hardcoding localhost ports — causes CI failures when ports are in use',
|
|
114
|
-
'Not testing the unhappy auth paths — security gap',
|
|
115
|
-
'Asserting only on status codes, not response body — misses contract violations',
|
|
116
|
-
],
|
|
117
|
-
e2e: [
|
|
118
|
-
'Using CSS class selectors — they change on style refactors and break tests',
|
|
119
|
-
'Sharing state between tests — one failure cascades to all subsequent tests',
|
|
120
|
-
'Testing every UI permutation — creates a massive suite that nobody maintains',
|
|
121
|
-
'Ignoring flaky tests — they erode trust in the entire suite',
|
|
122
|
-
'Not running e2e on CI — catching bugs only locally defeats the purpose',
|
|
123
|
-
],
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const typePatterns: Record<TestType, string[]> = {
|
|
127
|
-
unit: ['Arrange-Act-Assert', 'Mock object pattern', 'Test data builder', 'Parameterised tests', 'Given-When-Then'],
|
|
128
|
-
integration: ['Test container pattern', 'Database cleaner pattern', 'Fixture factory', 'Contract testing', 'Seeding pattern'],
|
|
129
|
-
e2e: ['Page Object Model', 'Journey test pattern', 'Screenplay pattern', 'Seed-and-clean pattern', 'Visual regression testing'],
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const typeDuration: Record<TestType, string> = {
|
|
133
|
-
unit: '1-3 hours',
|
|
134
|
-
integration: '3-6 hours',
|
|
135
|
-
e2e: '4-8 hours',
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
export function plan(task: string, context?: string): AgentPlan {
|
|
139
|
-
const testType = detectTestType(task, context);
|
|
140
|
-
return {
|
|
141
|
-
agent: 'tester' as WorkerAgentType,
|
|
142
|
-
task,
|
|
143
|
-
tier: 2,
|
|
144
|
-
approach: typeApproach[testType],
|
|
145
|
-
steps: typeSteps[testType],
|
|
146
|
-
checklist: typeChecklist[testType],
|
|
147
|
-
pitfalls: typePitfalls[testType],
|
|
148
|
-
patterns: typePatterns[testType],
|
|
149
|
-
duration_estimate: typeDuration[testType],
|
|
150
|
-
};
|
|
151
|
-
}
|