The triage processor was only checking the global semaphore, not the
per-project maxConcurrent setting. Now it counts all active agents
(in-progress + specifying) and respects Math.min(perProjectAvailable,
semaphoreAvailable) before starting new triage tasks. This matches the
scheduler's concurrency enforcement and prevents triage from consuming
all available slots on startup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The triage processor was kicking off all eligible tasks simultaneously,
queuing them all on the semaphore regardless of available slots. Now it
checks semaphore.availableCount first and only starts as many triage
tasks as there are free slots. Remaining tasks get picked up on the
next poll cycle (default 10-15s) as slots free up.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProjectEngineManager now creates a single AgentSemaphore and injects it
into all engines via config.globalSemaphore. Previously each engine created
its own semaphore, so the globalMaxConcurrent limit was not enforced across
projects. The semaphore dynamically reads the limit and listens for
concurrency:changed events for live updates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add PUT /api/global-concurrency endpoint to update globalMaxConcurrent
via CentralCore (validated 1-50 range)
- Make InProcessRuntime semaphore react to live concurrency changes via
CentralCore "concurrency:changed" event — no restart needed
- Add Global Max Concurrent input to Settings modal Scheduling section
with fetch-on-mount and save-alongside-project-settings behavior
- Add updateGlobalConcurrency API client function
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the anti-pattern where the cwd project was treated as "primary" with a
special engine, and other projects got "secondary" engines through a separate
code path. Every project now gets an identical ProjectEngine created through
ProjectEngineManager.
Key changes:
- Add ProjectEngineManager class to @fusion/engine for uniform engine lifecycle
- Replace manual engine maps in dashboard.ts and serve.ts with engineManager
- Add engineManager to ServerOptions for per-project engine resolution
- Add getProjectContext() helper in routes.ts (replaces 199 getScopedStore calls)
- Merge and automation routes now resolve engine subsystems per-request
- SSE endpoint uses engine's store when available (same EventEmitter)
- Fix tsx not found in dev-with-memory.mjs startup script
- Add invalidateAllGlobalSettingsCaches for cross-project settings sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docs(FN-1626): document PWA home bar gap token and updated bottom-layout contract
- fix(pre-existing): add missing invalidateAllGlobalSettingsCaches export to unblock build
- feat(FN-1626): update regression tests for standalone spacing and PWA home bar gap
- feat(FN-1626): add PWA home bar gap token and update bottom-layout CSS contract
Background stale spec checks now skip tasks that are already in an
active work state (in-progress, in-review, done, merging, merging-pr)
to avoid interrupting work that is underway or complete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a merge completes but auto-recovery moves the task back to
in-review, the retry gating (mergeRetries >= 3) blocked re-processing.
Now canMergeTask always accepts mergeConfirmed tasks and drainMergeQueue
fast-paths them directly to done without re-running the merge agent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add spec-staleness evaluator to check task specification age before execution
- Guard executor startup and resume to prevent running tasks with stale specs
- Guard scheduler dispatch to skip stale tasks and move them back to triage
- Add comprehensive tests for spec staleness detection (7d/14d thresholds)
- Add getFusionDir mock for executor staleness check tests
- Replace blocking execSync calls with async execAsync in executor.ts for user-configured commands
- Convert self-healing.ts worktree status checks to async to avoid blocking the event loop
- Update step-session-executor.ts to use async worktree operations
- Refactor worktree-pool.ts for fully async worktree creation, cleanup, and listing
- Convert pi.ts agent session handling to async execution
- Update all corresponding tests with async/await patterns
- Add changeset for @gsxdsm/fusion patch release
- Add context-overflow detection and recovery in step-session executor with same parity as single-session
- Implement step resumption from overflow checkpoints with accumulated context replay
- Add overflow state tracking and recovery logging for diagnostics
- Harden single-session executor overflow recovery with improved state management
- Update appendAgentLog parameter signatures across executors
- Add comprehensive tests for context overflow scenarios in both execution modes
- Add memory entry documenting the unified context-limit recovery approach
- Add eslint-disable comments for unused parameters in truncateVerificationOutput and summarizeVerificationOutput
- Preserve functions that may be useful for future diagnostics while silencing no-unused-vars warnings
- Add summarizeVerificationOutput helper function to condense test output
- Update runVerificationCommand to use summarization for cleaner merge reports
- Improve readability of test failure summaries in the dashboard
- Convert hasRecoverableGitWork from sync execSync to async execAsync
- Update recoverNoProgressNoTaskDoneFailures caller to await the method
- Update self-healing tests to work with async method and execAsync mock
- Add exec and promisify imports to self-healing.ts
- Fix mock to include exec export for promisify compatibility
The FN-1598 merger.ts async conversion introduced an escalation strategy where
attempt 3 uses -X theirs and succeeds when AI fails. The tests were expecting
the merge to fail after 3 AI attempts, but now attempt 3 succeeds via the
theirs strategy.
This commit fixes the test mocks to make the -X theirs merge fail so all
3 attempts exhaust and the function throws as expected.
Tests cover:
- parseValidationResult with JSON extraction from markdown code blocks and malformed responses
- handleValidationPass verifying feature marked 'passed' and autopilot notified
- handleValidationFail verifying fix feature generated and retry budget decremented
- handleValidationBlocked verifying feature marked 'blocked' without fix generation
- Retry budget enforcement preventing further implementations when exhausted
- recoverActiveMissions verifying actual processTaskOutcome calls for features in validating/needs_fix states
Added 23 new tests (1916 total, up from 1893).
- Implement actual AI response parsing in parseValidationResult() with JSON extraction
from markdown code blocks, repair for common JSON issues, and assertion result parsing
- Fix notifyValidationComplete to pass feature.taskId instead of featureId to
handleTaskCompletion() in in-process-runtime, dashboard, and serve
- Fix recoverActiveMissions() to actually transition validating features back to
implementing and call processTaskOutcome for features with completed tasks
- Add comprehensive unit tests for MissionExecutionLoop lifecycle, processTaskOutcome,
recoverActiveMissions, and error handling
The merger's test/build verification, executor's worktreeInitCommand,
setupScript, and script-mode workflow steps all used execSync, which
blocks Node's event loop until the child process exits. A hanging
pnpm test could freeze the entire engine (no logs, heartbeats, or
other task progress) for the full 5-minute timeout.
Switch these call sites to promisify(exec) with awaited calls so the
engine keeps running while user-configured commands execute. Short
internal git plumbing (rev-parse, branch -d, worktree remove) still
uses execSync since those commands are bounded and measured in ms.
Document the rule in AGENTS.md under a new "Engine process rules"
section so future agents don't reintroduce blocking behavior.
Tests: update child_process mocks in merger.test, executor.test, and
restart.integration.test to route the new async exec through the
existing execSync mock and expose promisify.custom so destructuring
{ stdout, stderr } matches real child_process.exec semantics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit adds the schema migration and types for the mission execution loop validation system:
- Adds loop state tracking columns to mission_features table (loopState, implementationAttemptCount, validatorAttemptCount, lastValidatorRunId, lastValidatorStatus, generatedFromFeatureId, generatedFromRunId)
- Creates mission_validator_runs table for tracking validation runs
- Creates mission_validator_failures table for assertion failure records
- Creates mission_fix_feature_lineage table for tracking fix feature relationships
- Adds workflowStepRetries column to tasks table for retry tracking
- Adds FEATURE_LOOP_STATES and VALIDATOR_RUN_STATUSES enums
- Updates TaskStore to support workflowStepRetries field
- Updates TaskExecutor to handle workflow step failures with retry logic
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Detect when merge prompt exceeds context limit and gets truncated
- Retry merge with compacted prompt when truncation is detected
- Create fresh session after context limit recovery
- Add comprehensive tests for truncation detection and recovery flow
- Update merge prompt generation to include truncation signal
Deterministic verification failures were embedding the raw stderr/stdout
(up to 50MB per VERIFICATION_COMMAND_MAX_BUFFER) in a second log entry,
flooding logs/stdout and crashing the app. The runVerificationCommand
helper already wrote a truncated summary, so verifyDeterministicBuild
now just references it.
When the failure surfaces in the dashboard merge handler, kick the task
back to in-progress with a steering comment so the agent can fix the
failing test/build instead of parking it in in-review with a fatal
error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update project memory instructions to encourage selective writes instead of unconditional appends
- Instruct agents to consolidate existing entries rather than add duplicates
- Add guidance to skip memory updates when no durable learnings were discovered
- Clarify what qualifies as durable learnings vs task-specific trivia
- Update user-facing memory documentation to reflect new selective behavior
- Add test coverage for selective memory write instructions
Changed @testing-library/jest-dom import to @testing-library/jest-dom/vitest
in vitest.setup.ts to properly provide type definitions for jest-dom matchers
like toBeInTheDocument, toHaveClass, toBeDisabled, and toHaveAttribute in the
vitest environment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bundles staged work-in-progress modifications across multiple packages
(routes, store, agent-instructions, self-healing, QuickEntryBox, etc.)
plus the dashboard theme-data.css preload fix.
Note: an unstaged 621-line deletion in .fusion/memory.md was deliberately
NOT committed — it appears to be an accidental overwrite of architecture
notes and is left in the working tree for review.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>