Commit Graph

2768 Commits

Author SHA1 Message Date
gsxdsm
cd178261ec Merge remote-tracking branch 'origin/main' into latest2-1717
# Conflicts:
#	packages/engine/src/__tests__/executor-workspace.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/base-commit-capture.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 18:13:41 -07:00
gsxdsm
0ebc9ea252 Merge branch 'main' into feature/add-request-headers-x-session-id-and-x-session-a 2026-06-23 18:03:33 -07:00
gsxdsm
9fbc93c8b1 Merge remote-tracking branch 'origin/main' into latest-1717
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 17:40:35 -07:00
gsxdsm
fa622721e8 Merge remote-tracking branch 'origin/main' into latest-1713
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 17:38:37 -07:00
gsxdsm
8d343b87b3 Merge branch 'main' into feature/add-request-headers-x-session-id-and-x-session-a 2026-06-23 17:37:57 -07:00
gsxdsm
e17e9bc867 feat(#1675): add X-Session-Id and X-Session-Affinity routing headers to LLM requests
Add X-Session-Id and X-Session-Affinity headers to all outbound LLM chat
completion requests so LLM gateways can sticky-route consecutive requests
from the same conversation and observability tools (Langfuse, Arize) can
group stateless API calls into a single multi-turn trace.

The headers carry a stable identifier: the task id when available (stable
across pause/resume), otherwise the pi session id. The implementation wraps
modelRegistry.getApiKeyAndHeaders -- the single chokepoint pi-coding-agent
uses for both the main stream and compaction -- merging routing headers into
the resolved output. This covers all HTTP-based providers (built-in, custom,
and HTTP-streaming extensions) without disturbing auth resolution.

Also propagates taskId to four secondary executor sessions (retry,
verification-fix, workflow-step, child-agent) that previously fell back to
a per-instance pi id, fragmenting per-task observability grouping.

Closes #1675
2026-06-23 17:31:52 -07:00
gsxdsm
37b2cb38ac Merge branch 'main' into feature/workflow-branch-group 2026-06-23 17:16:16 -07:00
gsxdsm
0dea25061d Merge remote-tracking branch 'origin/main' into conflict-resolution-1717
# Conflicts:
#	packages/engine/src/__tests__/executor-recovery.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/executor.ts
#	packages/engine/src/merger-ai.ts
#	packages/engine/src/project-engine.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 16:12:10 -07:00
gsxdsm
dcee0d3172 Merge remote-tracking branch 'origin/main' into conflict-resolution-1713
# Conflicts:
#	packages/engine/src/__tests__/executor-recovery.test.ts
#	packages/engine/src/agent-tools.ts
#	packages/engine/src/executor.ts
#	packages/engine/src/worktree-acquisition.ts
2026-06-23 15:56:06 -07:00
gsxdsm
4faadd4f75 Merge remote-tracking branch 'origin/main' into conflict-resolution-1712
# Conflicts:
#	packages/dashboard/app/components/WorkflowNodeEditor.tsx
#	packages/engine/src/__tests__/executor-fast-mode-workflows.test.ts
2026-06-23 15:44:52 -07:00
gsxdsm
a8abecd043 Merge remote-tracking branch 'origin/main' into conflict-resolution-1711
# Conflicts:
#	packages/engine/src/executor.ts
2026-06-23 15:37:29 -07:00
gsxdsm
905a877954 Merge pull request #1710 from MichaelHoughtonDeBox/feat/workspace-multi-repo
Workspace mode: open a folder of git repos as one project (foundation + design Q)
2026-06-23 15:08:15 -07:00
gsxdsm
bac7049822 chore(release): v0.46.0
Version bump via changesets.
2026-06-23 12:27:50 -07:00
gsxdsm
fe536b2af8 FN-6954: reconcile stale parked task assignments
Reconcile agent/task drift when durable agents remain linked to queued tasks without live execution proof.

- clear stale Agent.taskId links for parked todo/triage tasks while preserving task leases and queue state
- report stale parked assignments as active/no-live-run in Reports Health Check before reconciliation completes
- add scheduler and self-healing coverage for queued lease drift, overlap starvation, and audit events
- document the reconciliation behavior and add a published package patch changeset

Files changed:
 .changeset/fn-6954-agent-task-state-drift.md       |   5 +
 docs/architecture.md                               |   2 +
 .../src/__tests__/heartbeat-executor.test.ts       |  68 ++++++++++++
 .../__tests__/scheduler-overlap-starvation.test.ts |  68 +++++++++++-
 .../self-healing-agent-link-drift.test.ts          |  97 ++++++++++++++++-
 .../engine/src/__tests__/task-agent-sync.test.ts   |  33 +++++-
 packages/engine/src/agent-heartbeat.ts             | 121 +++++++++++++++++++--
 packages/engine/src/run-audit.ts                   |   5 +
 packages/engine/src/runtimes/in-process-runtime.ts |   1 +
 packages/engine/src/scheduler.ts                   |  24 +++-
 packages/engine/src/self-healing.ts                | 102 ++++++++++++++---
 packages/engine/src/task-agent-sync.ts             |  65 ++++++++++-
 12 files changed, 555 insertions(+), 36 deletions(-)

Fusion-Task-Id: FN-6954
Fusion-Task-Lineage: 24b8a2eb-5a33-4539-ab64-ae2bbfc2d195
2026-06-23 10:30:50 -07:00
gsxdsm
81f43e0e89 Merge pull request #1730 from Runfusion/feature/memory-fix
[codex] Dispose completed spawned child sessions
2026-06-23 10:29:30 -07:00
gsxdsm
a46800128a Address PR review feedback (#1730)
- Await async spawned child session disposal

- Use own-key iteration for structured tool result previews

- Add FNXC requirement comments for new regression assertions
2026-06-23 09:57:49 -07:00
gsxdsm
e59b1fa07d Merge pull request #1726 from plarson/fix/triage-spec-review-fallback
fix(triage): pass fallback model settings to spec review
2026-06-23 09:52:58 -07:00
gsxdsm
36b7ad90fd fix(engine): bound structured tool result log previews 2026-06-23 09:19:32 -07:00
gsxdsm
466cf9ca8d fix(engine): dispose completed spawned child sessions 2026-06-23 09:12:28 -07:00
Phil Larson
823623e1c6 fix(triage): address spec review fallback PR comments 2026-06-23 08:51:58 -07:00
Phil Larson
40bef6dfce fix(triage): pass fallback model settings to spec review 2026-06-23 08:39:27 -07:00
gsxdsm
a670f5ce98 FN-6952: repair workflow-column lifecycle regressions
Restores core lifecycle compatibility while keeping workflow-column scheduling on the graduated runtime.\n\n- Split raw compatibility-flag checks from public workflow-column runtime enablement.\n- Keep scheduler and hold-release sweeps on workflow columns despite stale persisted false flags.\n- Preserve legacy moveTask guard bypass and invalid-transition behavior for compatibility paths.\n- Isolate the startup watch recovery fixture and add a patch changeset.\n\nFiles changed:\n .changeset/fn-6952-core-lifecycle-regressions.md |  5 ++++\n packages/core/src/__tests__/store-create.test.ts |  6 +++++\n packages/core/src/store.ts                       | 32 +++++++++++++++++-------\n packages/core/src/workflow-columns-settings.ts   |  8 ++----\n packages/engine/src/hold-release.ts              |  6 +++--\n packages/engine/src/scheduler.ts                 | 10 +++++++-\n 6 files changed, 49 insertions(+), 18 deletions(-)

Fusion-Task-Id: FN-6952

Fusion-Task-Lineage: cfd7ad99-db00-4454-b3d7-a07a72a7bbe7
2026-06-23 08:21:10 -07:00
gsxdsm
b599b6ab41 chore(release): v0.45.0
Version bump via changesets.
2026-06-23 01:07:59 -07:00
gsxdsm
9218613552 FN-6949: fix auto-merge lifecycle finalization
Make auto-merge completion idempotent when landed tasks are left outside done.

- Add shared auto-merge finalization logic that refreshes task rows, clears stale queue/blocker state, and recovery-rehomes proven landed tasks to done.
- Route direct AI merge, merge-confirmed fast paths, and self-healing recovery through the shared finalizer with run-audit events for reconciled or blocked column mismatches.
- Extend lifecycle and recovery tests to cover todo/in-review mismatches, finalization failures, stale blockers, and cleanup idempotency.
- Add the required patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-6949-auto-merge-lifecycle.md         |   5 +
 .../src/__tests__/merge-error-recovery.test.ts     |  50 ++++-
 packages/engine/src/__tests__/merger-ai.test.ts    |   8 +-
 .../src/__tests__/merger-merge-lifecycle.test.ts   | 155 +++++++++++++++
 .../ai-merge-cleanup-enoent-idempotent.test.ts     |   2 +-
 packages/engine/src/__tests__/self-healing.test.ts | 194 +++++++++++--------
 packages/engine/src/auto-merge-finalization.ts     | 208 +++++++++++++++++++++
 packages/engine/src/merger-ai.ts                   |  47 +++--
 packages/engine/src/project-engine.ts              |  87 +++++----
 packages/engine/src/run-audit.ts                   |   4 +
 packages/engine/src/self-healing.ts                |  92 ++++-----
 11 files changed, 663 insertions(+), 189 deletions(-)

Fusion-Task-Id: FN-6949

Fusion-Task-Lineage: db784ec0-68e9-4a37-9728-daa6f133e294
2026-06-22 23:18:28 -07:00
gsxdsm
acf0fff413 test(engine): cover workflow cutover recovery guards 2026-06-22 22:22:37 -07:00
gsxdsm
e60b1378b8 fix(engine): address workflow cutover review feedback 2026-06-22 22:17:30 -07:00
gsxdsm
65c4dc5438 fix(engine): harden workflow runtime cutover 2026-06-22 21:45:05 -07:00
gsxdsm
bf3276295c fix(engine): complete workflow runtime cutover 2026-06-22 20:45:29 -07:00
gsxdsm
f9043d733e fix(dashboard): polish workflow and chrome defaults 2026-06-22 19:48:50 -07:00
gsxdsm
ec1d29e818 FN-6922: guard worktree acquisition returns from repo root
Prevent task worktree acquisition from handing the project root back to executors.

- Add a repo-root return guard across resume, pool, and fresh acquisition paths.
- Clear invalid repo-root task assignments and create a fresh worktree fallback when safe.
- Share repo-root canonicalization and expand regression coverage for acquisition liveness.
- Document the acquisition guard and add a patch changeset.

Files changed:
 .changeset/fn-6922-repo-root-acquisition-guard.md  |   5 +
 .../repo-root-task-worktree-requeue-loop.md        |   9 +-
 .../__tests__/executor-worktree-liveness.test.ts   |  35 +++
 .../worktree-acquisition-secrets-env.test.ts       |   9 +-
 .../src/__tests__/worktree-acquisition.test.ts     |  91 ++++++-
 packages/engine/src/worktree-acquisition.ts        | 272 +++++++++++++--------
 packages/engine/src/worktree-pool.ts               |   8 +-
 7 files changed, 304 insertions(+), 125 deletions(-)

Fusion-Task-Id: FN-6922

Fusion-Task-Lineage: c16cee1b-de16-4d5f-90e0-132f94fa8377
2026-06-22 19:11:08 -07:00
gsxdsm
4fd8d444fb fix(dashboard): polish app chrome and workflow defaults 2026-06-22 18:21:06 -07:00
gsxdsm
c229a15e20 FN-6934: restrict agent workflow reassignment
Clarify agent workflow-routing prompts so agents do not reassign tasks they did not create unless directed.

- Add executor guardrails against changing the current task workflow without explicit user instruction.
- Update triage workflow-routing guidance to distinguish existing tasks from agent-created tasks.
- Cover workflow ownership policy in prompt and engine tests, docs, and a patch changeset.

Files changed:
 .changeset/fn-6934-workflow-movement-policy.md     |  5 +++++
 docs/agents.md                                     |  6 ++++++
 docs/custom-workflow-reliability-acceptance-map.md |  7 +++++--
 docs/settings-reference.md                         |  9 +++++++--
 docs/workflow-steps.md                             |  9 +++++++--
 packages/core/src/__tests__/agent-prompts.test.ts  | 15 ++++++++++++++
 packages/core/src/agent-prompts.ts                 | 23 ++++++++++++++++------
 .../src/__tests__/executor-review-verdicts.test.ts |  3 +++
 .../__tests__/triage-threshold-settings.test.ts    |  3 +++
 packages/engine/src/__tests__/triage.test.ts       |  2 +-
 packages/engine/src/executor.ts                    |  5 +++++
 11 files changed, 74 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-6934

Fusion-Task-Lineage: cc583806-b160-4a08-b1a7-e798a575dbd5
2026-06-22 17:58:27 -07:00
gsxdsm
0b410d54e1 FN-6915: surface user comments in agent prompts
User task chat and steering comments now flow into planning, review, and merge agent context.

- Add a shared user-comment formatter and selector for prompt context.
- Inject latest user comments into planner-triggered review, reviewer, merger, and clean-room AI merge prompts.
- Cover comment selection, prompt formatting, review/merge propagation, and docs updates with targeted tests.

Files changed:
 docs/architecture.md                               |  1 +
 docs/dashboard-guide.md                            |  1 +
 .../src/__tests__/agent-user-comments.test.ts      | 81 ++++++++++++++++++++++
 .../executor-review-step-indexing.test.ts          | 28 +++++++-
 packages/engine/src/__tests__/merger-ai.test.ts    | 54 +++++++++++++++
 .../src/__tests__/merger-prompt-and-utils.test.ts  | 29 ++++++++
 packages/engine/src/__tests__/reviewer.test.ts     | 37 ++++++++--
 packages/engine/src/__tests__/triage.test.ts       | 23 ++++++
 packages/engine/src/agent-user-comments.ts         | 60 ++++++++++++++++
 packages/engine/src/executor.ts                    | 14 ++--
 packages/engine/src/merger-ai.ts                   | 23 +++++-
 packages/engine/src/merger.ts                      | 22 +++++-
 packages/engine/src/reviewer.ts                    | 13 ++++
 13 files changed, 372 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-6915

Fusion-Task-Lineage: 02de0c12-481a-4489-b495-70c9c00619bc
2026-06-22 03:34:15 -07:00
gsxdsm
af06170a51 FN-6778: add agent artifact registry tools
Adds engine and chat tools for registering, discovering, and viewing artifacts with inbox notifications.

- Add fn_artifact_register, fn_artifact_list, and fn_artifact_view tools for heartbeat, executor, and chat sessions.
- Send best-effort dashboard system inbox notifications when artifacts are registered.
- Classify artifact tools for action gating and coordination exemptions, with coverage for executor, heartbeat, permanent agent, and chat flows.
- Document the artifact registry behavior and update package metadata, quarantine ledger, and line-count baseline.

Files changed:
 .changeset/fn-6778-artifact-agent-tools.md         |   5 +
 CONCEPTS.md                                        |   3 +
 docs/agents.md                                     |   1 +
 packages/core/src/db.ts                            |   1 +
 .../src/__tests__/session-error-recovery.test.ts   |   1 +
 .../session-persistence-roundtrip.test.ts          |   1 +
 .../src/__tests__/session-reconnect.test.ts        |   1 +
 .../src/__tests__/session-resume-history.test.ts   |   1 +
 packages/dashboard/src/chat.ts                     |   6 +-
 packages/dashboard/src/planning.ts                 |   4 +
 packages/dashboard/src/test/mockCoreEngine.ts      |   1 +
 .../engine/src/__tests__/agent-action-gate.test.ts |   3 +
 .../src/__tests__/agent-artifact-tools.test.ts     | 458 +++++++++++++++++++++
 .../src/__tests__/executor-step-session.test.ts    |  48 +++
 .../src/__tests__/gating-classifications.test.ts   |   3 +
 .../src/__tests__/heartbeat-executor.test.ts       |  40 +-
 .../src/__tests__/heartbeat-session-prompt.test.ts |  25 +-
 .../src/__tests__/permanent-agent-gating.test.ts   |   6 +
 packages/engine/src/agent-heartbeat.ts             |   6 +-
 packages/engine/src/agent-tools.ts                 | 288 ++++++++++++-
 packages/engine/src/executor.ts                    |  25 ++
 packages/engine/src/gating-classifications.ts      |   7 +
 packages/engine/src/index.ts                       |   9 +
 scripts/lib/test-quarantine.json                   |   8 +-
 scripts/line-count-baseline.json                   |  54 +--
 25 files changed, 945 insertions(+), 60 deletions(-)

Fusion-Task-Id: FN-6778
Fusion-Task-Lineage: 7eb4afcb-8140-4f86-9540-eb3b83e64148
2026-06-22 03:34:14 -07:00
gsxdsm
3a71237624 fix(review): address PR #1717 Phase C merge-loop review feedback
- merger-ai: resolve+persist concrete landedSha when a sub-repo is recognized
  already-landed via the Fusion-Task-Id trailer fallback, so finalize no longer
  drops it and mis-finalizes a fully-landed workspace task as a no-op
- project-engine: manual-merge land-lease busy errors reject the resolver without
  burning mergeRetries; clear stale busy-reenqueue counter on real partial land;
  persist retry count before arming the backoff timer (fail closed on write error)
- cli/dashboard + task: use shared isWorkspaceTask predicate instead of inlining
- base-commit-capture: POSIX single-quote shell escaping for integration ref
- git-repository: validate workspace.json repos elements are strings
- merger-ai: drop dead store param from landOneRepo
- tests: assert the 60s backoff cap across cycles; exercise the real runAiMerge
  merge door; fix non-git-root assertion; re-export real workspace error classes
  in the merger-ai mock (fixes 24 pre-existing instanceof-undefined failures);
  remove generic fake-timer smoke test now covered by the live engine assertion

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 03:04:21 -07:00
gsxdsm
3fd7d12439 fix(review): address PR #1713 review feedback
Wrap the fatal-path acquisition observability writes (logEntry + audit.git)
in safeObserve so a store/audit throw can't replace the original
acquisition error, keeping WorkspaceRepoAcquireBusyError instanceof checks
reliable upstream.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 02:54:23 -07:00
gsxdsm
627bdcfb0a fix(review): Phase C merge-loop hardening — double-land, lease clobber, retry storm
5-persona review of the Phase-C per-repo merge loop. No P0; the no-push invariant
and retry/park accounting verified clean. Fixed:

Land mechanics (merger-ai.ts / active-session-registry.ts):
- persistRepoLandedSha no longer swallows the DB write: a failed landedSha write
  after the ref advanced now escalates to WorkspacePartialLandError so the engine
  parks/retries instead of silently re-landing (duplicate squash). isRepoLanded
  gains a landedSha-independent fallback — it scans the integration ref for this
  task's Fusion-Task-Id trailer (a squash commit is NOT a branch descendant, so a
  branch-ancestor check is provably wrong), so an actually-landed repo is skipped
  on retry.
- The land lease is now taskId-aware across kinds: any foreign-task holder on a
  sub-repo path is contention (a merging task can't run over an executing task's
  acquire lease), and registerPath throws ActiveSessionPathHeldByForeignTaskError
  instead of silently clobbering a different task's entry.
- The per-repo loop is wrapped in try/finally(setStatus(null)) so the busy/partial
  throws can't leave the task stuck 'merging'. WorkspacePartialLandError is a real
  exported class (not a .name-mutated Error). finalizeWorkspaceTask re-reads fresh
  and no longer swallows the mergeDetails write (TOCTOU). isRepoLanded exported for
  Phase D.

Dispatch + doors (project-engine.ts / dashboard.ts / task.ts / @fusion/core):
- getTask-null in the partial-land catch fails closed (park) instead of defaulting
  retries to 0 and scheduling an indefinite retry storm.
- The merge-confirmed reachability fast-path skips workspace tasks (its
  representative commitSha is a sub-repo squash sha, unreachable in the root cwd —
  it was demoting fully-merged tasks); they're verified by per-repo landedSha.
- The CLI/dashboard merge doors now return merged:true on full land (were hardcoded
  merged:false). WorkspaceRepoLandBusyError re-enqueues with backoff WITHOUT burning
  the mergeRetries quota (bounded busy counter) so contention can't park a healthy
  task. Backoff capped at 60s. shouldRetryWorkspacePartialLand folded into
  shouldRetryAutoMergeConflict. Catch switched to instanceof. New canonical
  isWorkspaceTask predicate in @fusion/core.

Gate green: build, typecheck, lint, test:gate (649+58); workspace-merger + oracle
+ project-engine 174.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 01:11:52 -07:00
gsxdsm
f2c1a28eab fix(workspace): re-read task before workspaceWorktrees merge; FNXC format
Addresses the follow-up review on the foundation fixes:
- Re-read the task via store.getTask immediately before merging the per-repo
  entry, so a concurrent sibling-repo acquisition that landed since the initial
  read isn't clobbered by updateTask's wholesale map replace (narrows the
  read-modify-write window to the store lock; a fully atomic per-repo store-level
  merge remains a follow-up).
- Normalize the inline FNXC comment to the FNXC:Area yyyy-MM-dd-hh:mm: convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:26:40 -07:00
gsxdsm
9f0492e69f fix(review): address PR #1713 review findings
- base-commit-capture.ts: shell-quote integration branch with a real
  single-quoted POSIX literal instead of JSON.stringify (not shell-safe).
- TaskCard.tsx: memo compares full workspaceWorktrees values, not just key
  sets, so a same-key worktreePath/branch change re-renders.
- TaskDetailModal.tsx: gate/render workspace summary off hydrated workingTask.
- worktree-acquisition.ts: null the singular worktree/branch columns in the
  workspaceWorktrees write so isWorkspaceTask stays true; wrap non-fatal
  post-acquire observability so logEntry/audit can't re-escalate to fatal.
- agent-tools.ts: register sub-repo worktree via onAcquired unconditionally
  (idempotent) so a resumed/already-acquired path is tracked after restart.
- executor.ts: DB liveness fallback also checks task.workspaceWorktrees paths.
- executor-workspace.test.ts: root non-git assertion runs in fx.rootDir (".").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:22:48 -07:00
gsxdsm
accb32e9b6 fix(review): address PR #1711 review findings
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:19:15 -07:00
gsxdsm
429258354d fix(workspace): address CodeRabbit review findings on the foundation
Resolves the actionable CodeRabbit threads on the workspace-mode foundation:

- project-resolver: defer saveWorkspaceConfig until after the user confirms init
  and store.init() succeeds (no partial .fusion/ on a declined/non-interactive run).
- git-repository: validate each candidate with a real `git rev-parse` work-tree
  probe before counting it (no false-positive repos from stray .git markers);
  loadWorkspaceConfig now rejects absolute paths, `..` escapes, and non-string
  entries so a corrupt/malicious config can't resolve outside the workspace root.
- executor: gate workspace mode on repos.length > 0 at all three sites so an
  empty { repos: [] } can't bypass the git-repo guard or enable an empty workspace.
- worktree-acquisition: thread the configured-command runner through the workspace
  acquire path (sub-repos run their init setup); validate repoRelPath as an in-root
  relative path before joining; liveness-check a remembered worktree before
  reporting it ready (pruned paths fall through to re-acquire); clear the singular
  task.worktree/branch after persisting per-repo state (per-repo state lives only
  in workspaceWorktrees).
- agent-tools: forward runContext into acquireWorkspaceRepoWorktree for log attribution.

The executor-workspace test's mock-the-subject pattern is left for the
session-scoping follow-up that rewrites it with a real two-repo fixture (FN-5048).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:09:59 -07:00
gsxdsm
64e87f9a12 feat(workspace): Phase C U3 — per-repo land lease (serialize same-sub-repo lands)
landWorkspaceTask now holds a per-repo land lease around each landOneRepo call:
a new activeSessionRegistry kind "workspace-repo-land" keyed on the sub-repo
absolute path, registered synchronously before the per-repo try and released in
a finally (on success and failure, only yanking our own taskId+ownerKey entry —
never a foreign/different-kind entry). Two workspace tasks landing the same
sub-repo serialize; the loser throws the retryable WorkspaceRepoLandBusyError,
which reuses the U2 partial-land retry/park machinery (consume a mergeRetry,
backoff re-enqueue up to MAX skipping landed repos, then operator-park). Disjoint
sub-repos never falsely serialize.

The lease is for serialization / clean-room-collision avoidance, not ref
correctness — advanceIntegrationBranchRef's CAS already makes interleaved
update-ref safe. Distinct from the execution-phase "workspace-repo-acquire" lease
(different kind, different lifecycle phase, each ignores the other's entry).

3 new tests (serialize, independence, release-on-failure); oracle (56) + U1/U2
(idempotency) stay green. Gate: build, typecheck, lint, test:gate (649+58).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:56:41 -07:00
gsxdsm
7544346320 feat(workspace): Phase C U2 — per-repo landed predicate, finalize-once, auto-retry-then-park
landWorkspaceTask now tracks per-repo landing and finalizes the task exactly once.
After a repo lands, its advanced integration tip is persisted as
workspaceWorktrees[repo].landedSha (fresh-read merge, siblings untouched). Before
landing, isRepoLanded skips a repo iff its landedSha is present AND an ancestor of
(or equal to) its local integration ref — so a retry after a partial land never
re-advances an already-landed ref. finalizeWorkspaceTask runs only when every
acquired repo is landed: it builds an aggregate MergeResult (representative
commitSha + a workspaceLandedShas map in MergeDetails) and calls the existing
task-global finalizeTask once, satisfying the task:merged consumer. No premature
done on the first repo.

Partial lands surface as WorkspacePartialLandError; the engine consumes a
mergeRetry and re-enqueues landWorkspaceTask (skipping landed repos) with the
existing conflict-retry backoff up to MAX, then operator-parks (status:failed) —
mirroring shouldRetryAutoMergeConflict (new exported shouldRetryWorkspacePartialLand
seam). The defense-in-depth WorkspaceTaskMergeError still hard-fails without
burning retries; manual merges fall through to rejectMergeResolvers.

types: workspaceWorktrees entry gains landedSha?; MergeDetails gains
workspaceLandedShas?. 6 new idempotency/predicate/finalize-once/retry-park tests;
oracle (52) + U1 (5) stay green. Gate: build, typecheck, lint, test:gate (649+58).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:46:44 -07:00
gsxdsm
79e53e8d97 fix(workspace): remove unused acquireWorkspaceRepoWorktree import
The foundation imports acquireWorkspaceRepoWorktree in executor.ts but
deliberately stops before wiring it into the executor lifecycle, so the import
is unused and fails @typescript-eslint/no-unused-vars (the sole Lint failure on
this PR). Remove the dead import; it is reintroduced with real usage in the
session-scoping follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:37:12 -07:00
gsxdsm
744ed098a5 feat(workspace): Phase C U1 — per-repo merge loop (landOneRepo + landWorkspaceTask)
Extracts the per-repo land mechanics out of runAiMerge's inline clean-room
closure into an exported landOneRepo(store, repoRootDir, branch, integrationBranch,
ctx): pre-merge prune (rooted at the sub-repo), the clean-room temp worktree,
mergeAndReview, landSquash, and the CAS concurrent-advance retry that advances ONE
local integration ref — no remote push. runAiMerge is rewired as the single-repo
caller (its task-global finalization unchanged); the merger-ai suite (56 tests)
stays green as the byte-for-byte oracle.

landWorkspaceTask loops a workspace task's acquired sub-repos (sorted keys),
re-resolving each repo's integration branch with the shared override stripped
({...settings, integrationBranch: undefined, baseBranch: undefined}) so each
sub-repo lands on its own origin/HEAD, calls landOneRepo per repo, and aggregates
repo-tagged results — land-as-you-go on each repo's LOCAL ref (D2/D5). It does NOT
finalize/move the task (finalize-once + landed-tracking + idempotent retry are U2).

Door routing (KTD2): the engine dispatch and the user-facing CLI `fn task merge`
+ dashboard merge doors route workspace tasks to landWorkspaceTask so manual merge
works; store.mergeTask, aiMergeTask, and the runAiMerge chokepoint guard keep
throwing WorkspaceTaskMergeError as defense-in-depth.

New two-repo fixture tests: both repos land + no-push assertion, per-repo
override-stripped resolution onto distinct branches, repo-B conflict partial land
(task not moved), defense-in-depth throws. Gate green: typecheck, lint, build,
test:gate (649+58).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:31:43 -07:00
gsxdsm
453ed92dbf fix(review): Phase B workspace hardening — fail-closed scope guard, review conjunction, .changeset carve-out
ce-code-review (4 personas) on Phase B. No P0; the review conjunction was
confirmed safe (no false-done — empty map and per-repo throws both route to
UNAVAILABLE, which blocks). Applied:

P1: the fn_task_done scope-leak guard now fails CLOSED in workspace mode — a
per-repo capture throw blocks completion ("refusing as a precaution") instead of
the outer .catch returning {blocked:false} and letting an incomplete check pass.
A scoped task that acquired ZERO sub-repo worktrees is now blocked rather than
silently passing scope enforcement.

P2: reviewWorkspacePerRepo breaks on the first non-APPROVE repo so a later repo's
throw can't discard an already-determined REVISE (callers were seeing UNAVAILABLE
instead). captureWorkspaceModifiedFiles isolates each per-repo capture in
try/catch so one repo's throw can't skip the modifiedFiles write. The .changeset
always-allowed carve-out is honored in workspace mode: the scope-leak branch now
filters repo-LOCAL paths via the (previously dead) workspace-paths.ts
deriveRepoScopeSubset helper through the same filter as the singular path, so a
sub-repo .changeset/* no longer falsely blocks fn_task_done. All four per-repo
loops iterate sorted keys for deterministic offending-repo reporting; the dead
repoRel callback param and the duplicate path-normalizer are removed.

Verified safe (no change): the reviewer semaphore releases on throw (try/finally),
and per-repo reviewers inherit the task abort via session disposal.

Deferred to Phase C: extracting a workspace-executor.ts module (before the merge
loop lands). Gate green: typecheck, lint, build, test:gate (649+58).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:02:46 -07:00
gsxdsm
81edbeefbd feat(workspace): Phase B U2 — per-repo review (both sites) + fn_task_done verify + scope-leak
In workspace mode both review entry points and the completion guards now iterate
every acquired sub-repo. A shared reviewWorkspacePerRepo loops task.workspaceWorktrees
and invokes the existing single-cwd reviewStep once per repo (cwd = the sub-repo —
the reviewer agent runs its own git diff there), aggregating repo-tagged verdicts
as a conjunction: the task is reviewed only if every repo APPROVEs; the first
non-APPROVE repo's verdict becomes the aggregate. Both call sites loop — the
in-session fn_review_step tool AND the step-inversion seam (createReviewStepTool
and the stepReview workflow seam) — so no review surface silently scopes to the
non-git root (FN-5893). reviewStep itself stays single-cwd; the callers loop.

fn_task_done completion verification iterates per repo: verifyWorktreeInvariants
(from U1) already covers all worktrees, and evaluateTaskDoneScopeLeak now loops
each sub-repo (cwd + repo.baseCommitSha, repo-prefixed touched files vs the
repo-prefixed declared File Scope), blocking on the first repo with off-scope
files and naming it. Both return shapes preserved (ReviewResult; {blocked,message}).

New workspace-paths.ts repo-prefix helper (deriveRepoForPath/splitRepoScopedPath/
deriveRepoScopeSubset; segment-wise longest-prefix match, unscoped fallback) —
master U5 reuses it. Singular non-workspace path unchanged. 16 new fixture tests.
Gate green: typecheck, lint, build, test:gate (649+58).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:44:40 -07:00
gsxdsm
fc9423e465 feat(workspace): Phase B U1 — per-repo change capture, contamination, and verify
In workspace mode the executor now captures changes and verifies worktree
invariants per acquired sub-repo instead of degrading to empty against the
non-git root. Post-session capture (:7898) gains a workspace branch that loops
task.workspaceWorktrees and reuses captureModifiedFiles(repo.worktreePath,
repo.baseCommitSha, …) per repo — inheriting resolveDiffBaseRef's merge-base
fallback (repo baseCommitSha may be undefined) and the filterFilesToOwnTaskCommits
contamination/divergence audit — then prefixes each repo's files with the repo
path into task.modifiedFiles. Branch attribution runs per sub-repo (cwd), never
against the root. The no-op assertCleanBranchAtBase is not iterated.

verifyWorktreeInvariants is un-stubbed for workspace mode: it iterates every
workspaceWorktrees entry asserting toplevel match + HEAD on fusion/<id>, and
returns the FIRST failing repo while preserving the exact discriminated union
{ok:true} | {ok:false; reason:'wrong_toplevel'|'wrong_branch'|'no_commits';
observed; expected} (the :10889 consumer switches on reason for requeue/handoff)
— the new repo field is additive. Singular non-workspace path unchanged.

Real two-repo fixture tests (capture A+B repo-prefixed vs own base, undefined-base
fallback, foreign-commit contamination audit, wrong_branch verify failure,
single-repo regression). Gate green: typecheck, lint, test:gate (649+58).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:29:25 -07:00
gsxdsm
d5fa8654f7 fix(review): Phase A workspace hardening — tool errors, activeWorktrees, non-fatal acquire
ce-code-review (5 personas) on Phase A. No P0; the workspace-root-removal path
was ruled out and the contract changes verified additive. Applied:

P1: fn_acquire_repo_worktree now catches WorkspaceRepoAcquireBusyError (and
generic failures) and returns a sanitized retryable isError instead of an
uncaught throw into the agent loop; runContext is forwarded so audit/log keep
attribution. Per-repo acquired worktree paths are now registered into the
executor's activeWorktrees Set (via an onAcquired callback) — previously the Set
only held the browse-only root, making the U1 per-repo liveness invariant hollow.
Post-acquire identity-guard install and base-SHA capture are now non-fatal
(log-and-continue): a hook/branch failure no longer strands the on-disk worktree
(the worktree is usable without the guard; an undefined baseCommitSha is already
an accepted state).

P2: the KTD3 settings-strip also strips settings.baseBranch (resolveFromSettings
falls back integrationBranch → baseBranch, so a shared baseBranch leaked); the
workspaceWorktrees write re-reads the task fresh before merging to avoid a
sibling-repo clobber on sequential acquires (store-level atomic merge deferred to
Phase B); the busy-path logging is wrapped so it can't mask the busy error; the
TaskCard memo compares key-sets not counts; the stuck-kill no-op for workspace
tasks is now logged; the exclusivity check-then-act synchrony is documented.

Residuals (Phase B): per-repo worktree teardown, orphan-scan coverage, reaper
dedup, store-level atomic merge.

Gate green: typecheck, lint, build, test:gate (649+58), affected (25 + TaskCard 251).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:33:56 -07:00
gsxdsm
26c8d960a2 FN-6893: add editable built-in workflow prompts
Enable project-scoped prompt overrides for built-in workflows without allowing structural edits.

- Add workflow prompt override storage, normalization, and IR overlay support for prompt and gate nodes.
- Expose dashboard API routes and Workflow Node Editor controls to edit or reset built-in prompts.
- Cover override persistence, route behavior, editor flows, and engine workflow resolution with tests.
- Document editable built-in prompts and reset-to-default behavior.

Files changed:
 docs/dashboard-guide.md                            |   2 +-
 docs/settings-reference.md                         |   2 +
 docs/workflow-steps.md                             |  20 ++-
 .../__tests__/workflow-definition-store.test.ts    |  25 +++
 .../workflow-prompt-overrides-store.test.ts        | 190 ++++++++++++++++++++
 .../__tests__/workflow-prompt-overrides.test.ts    |  58 +++++++
 packages/core/src/db.ts                            |  35 +++-
 packages/core/src/index.ts                         |   7 +
 packages/core/src/store.ts                         |  97 ++++++++++-
 packages/core/src/workflow-ir-resolver.ts          |  21 ++-
 packages/core/src/workflow-prompt-overrides.ts     |  65 +++++++
 packages/dashboard/app/api/legacy.ts               |  34 ++++
 .../app/components/WorkflowNodeEditor.css          |  35 ++++
 .../app/components/WorkflowNodeEditor.tsx          | 191 ++++++++++++++++++++-
 .../__tests__/WorkflowNodeEditor.test.tsx          | 136 ++++++++++++++-
 .../src/__tests__/workflow-routes.test.ts          |  59 +++++++
 .../src/routes/register-workflow-routes.ts         |  86 +++++++++-
 .../workflow-prompt-overrides-resolution.test.ts   |  61 +++++++
 packages/i18n/locales/en/app.json                  |  14 +-
 packages/i18n/locales/es/app.json                  |  14 +-
 packages/i18n/locales/fr/app.json                  |  14 +-
 packages/i18n/locales/ko/app.json                  |  14 +-
 packages/i18n/locales/zh-CN/app.json               |  14 +-
 packages/i18n/locales/zh-TW/app.json               |  14 +-
 24 files changed, 1168 insertions(+), 40 deletions(-)

Fusion-Task-Id: FN-6893

Fusion-Task-Lineage: 961eb119-32e1-44c3-9b51-6edd982fa565
2026-06-21 21:23:41 -07:00