Forge Review: Prioritized Fix Plan & Recommendations
Forge — Prioritized Fix Plan & Strategic Recommendations
Based on a full code review of ~5,500 lines across 30+ source files, 104 passing tests, and 5 TypeScript errors. This article provides an actionable fix order and strategic guidance.
Immediate Fixes (Week 1)
These are blocking issues that must be resolved before any real-world usage.
1. Eliminate Command Injection Vectors
Files: src/agents/planner.ts, src/agents/implementer.ts
| Tool | Vulnerability | Fix |
|---|---|---|
glob (planner) | execAsync(\find . -name "${input.pattern}"`)` | Use Bun.Glob API or Bun.spawn(['find', '.', '-name', pattern]) |
git_create_branch | \git checkout -b ${branchName}`` | Bun.spawn(['git', 'checkout', '-b', branchName]) |
git_commit | \git commit -m "${message}"`` | Bun.spawn(['git', 'commit', '-m', message]) |
Pattern: Replace all template-literal shell commands with array-argument Bun.spawn(). This prevents argument escaping attacks entirely.
2. Fix SQL Injection in Checkpoint Storage
File: src/orchestrator/checkpoint.ts:95-104
Replace string interpolation with parameterized queries:
typescript// BEFORE (vulnerable) this.db.exec(`INSERT INTO checkpoints VALUES ('${cp.id}', '${cp.traceId}', ...)`); // AFTER (safe) this.db.prepare('INSERT INTO checkpoints VALUES (?, ?, ?, ?, ?)').run( cp.id, cp.traceId, cp.phase, JSON.stringify(cp.state), cp.timestamp.toISOString() );
3. Fix TypeScript Compilation Errors
File: src/safety/breakers.ts — 5 errors at lines 207, 336, 337, 366, 388
All are noUncheckedIndexedAccess issues. Fix with optional chaining or non-null assertions where the index is guaranteed.
4. Fix Sort Direction Bug
File: src/events/bus.ts:242
Change ORDER BY timestamp ASC to ORDER BY timestamp DESC in getLatestCheckpoint().
Short-Term Improvements (Weeks 2-3)
5. Wire CLI to Real Pipeline
File: src/cli/index.ts
Replace simulatePhase() calls with actual Pipeline execution:
typescript// Replace lines 168-194 with: const { Pipeline } = await import('../orchestrator/pipeline.ts'); const pipeline = new Pipeline(task, generateTraceId(), { usePiAgentCore: options.usePiCore, llmConfig: config.llm, }); const result = await pipeline.run();
6. Consolidate Duplicate Types
- Delete error classes from
src/types/index.ts - Use
src/core/errors.tshierarchy everywhere - Split
src/types/index.tsinto domain-specific files
7. Unify EventBus Implementations
Create a factory that returns the appropriate implementation:
typescriptexport function createEventBus(config: { persist: boolean; dbPath?: string }): EventBus { return config.persist ? new SqliteEventBus(config.dbPath) : new InMemoryEventBus(); }
8. Fix Config Merge Bug
File: src/core/config.ts:90
typescriptbreakers: { ...defaults.safety.breakers, ...overrides.safety?.breakers }
9. Wire Deployer Events
File: src/agents/deployer.ts:666-668
typescriptprivate async emit(type: string, data: Record<string, unknown>): Promise<void> { await this.context.bus.emit({ traceId: this.traceId, source: 'deployer', type: `deployer.${type}`, payload: data, }); }
Medium-Term Architecture (Weeks 4-6)
10. Centralize Tool Execution
Migrate inline tools from all 5 agents to the ToolRegistry:
- Register all tools in
src/tools/with proper categories - Agents request tools by name from registry
- Execute through a sandboxed runner with timeouts and resource limits
- Log all tool invocations to the event bus for audit
11. Replace better-sqlite3 with bun:sqlite
Per CLAUDE.md: prefer built-in bun:sqlite. This affects:
src/events/bus.tssrc/orchestrator/checkpoint.tssrc/memory/store.ts
12. Add Integration Tests
The 104 existing tests are all unit tests. Add:
- Pipeline end-to-end test (plan → impl → review → test) with mock LLM
- Bounce-back test (review rejects → impl → review approves)
- Checkpoint save/resume round-trip
- Beads pipeline with mock
bdCLI
13. Improve Memory Retrieval
Replace keyword-based DefaultMemoryStore.search() with:
- TF-IDF scoring as a minimum improvement
- Embedding-based similarity when an LLM with embedding support is configured
Strategic Recommendations
Architecture
-
Adopt the pi-agent-core path as primary — the
PiAgentAdapteris more robust than the BaseAgent loop, with better event bridging and cost tracking. Consider deprecating the BaseAgent path after the adapter is battle-tested. -
Make the ToolRegistry the single source of truth — every tool should be registered, versioned, and executed through the registry. This enables sandboxing, rate limiting, and audit trails.
-
Add structured LLM output — agents currently parse free-text LLM responses with fragile regex. Use JSON mode or function calling to get structured outputs.
Security
-
Audit all
Bun.spawn()andexec()calls — there are ~15 places where tools shell out. Each must use array arguments, not template strings. -
Add input validation at tool boundaries — even though Zod schemas exist, the actual tool execution functions don't always validate before shelling out.
Developer Experience
-
Add a
--jsonoutput mode to CLI — enables piping forge output to other tools. -
Standardize on ULID for all IDs — already a dependency, just not used consistently.
-
Add a
forge doctorcommand — checks for LLM API keys, bd CLI availability, SQLite, and other prerequisites.
Effort Estimates
| Priority | Items | Estimated Effort |
|---|---|---|
| Immediate (Week 1) | Injection fixes, TS errors, sort bug | 1-2 days |
| Short-term (Weeks 2-3) | CLI wiring, type consolidation, EventBus unification | 3-5 days |
| Medium-term (Weeks 4-6) | Tool centralization, bun:sqlite migration, integration tests | 1-2 weeks |
| Strategic | pi-agent-core primary, structured output, DX improvements | Ongoing |
Test & Quality Snapshot
| Metric | Value |
|---|---|
| Tests | 104 passing / 0 failing |
| Test files | 6 |
| Test runtime | 670ms |
| TypeScript errors | 5 (all in breakers.ts) |
| Source files | ~30 |
| Total LoC | ~5,500 |
| Dependencies | 14 runtime + 4 dev |