Add `cli-agent` as a task-execute executor kind. A workflow node with
`config.executor === "cli-agent"` drives an engine-owned CLI coding agent
through the execute step via the new cli-agent/task-session.ts orchestration:
spawn in the worktree, issue the hook token + write hook scripts, inject the
prompt after readiness, subscribe to the state machine, and resolve on a
positive completion signal (R20 gating). Config is snapshotted at launch; the
PTY is reaped (completed) at the in-review handoff.
Executor seam: runGraphCustomNode gains a cli-agent branch delegating to
runCliAgentNode; the hard-cancel/abort path (awaitAbortInFlightTaskWork +
abortAllInFlight) claims and SIGKILLs the CLI session as a first-class surface,
marking it killed (never resume-eligible). Re-entry kills any prior live
session and launches fresh; follow-up resumes the recorded native session id
when supported. A PTY-pool ceiling surfaces as a typed task value, not a stall.
Node-config typing extended minimally (WorkflowNodeExecutorKind /
WorkflowNodeExecutorConfig in @fusion/core).
Tests: cli-agent/__tests__/task-session.test.ts (12) and
__tests__/cli-agent-executor.test.ts (9) cover AE1/AE5, hard cancel, re-entry,
follow-up, config snapshot, ceiling, and the generic-tier confirm-advance path,
using scripted adapters + a mock PTY seam. Engine typecheck clean; full
src/cli-agent suite + new tests green (144 passing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- restore customFields on unarchive; reconcile all occupants on field-schema edits (store.ts)
- serialize per-field saves + controlled inputs in TaskFieldsSection (race fixes)
- fn_workflow_get includes layout; Array.isArray guards in validateCodeNodeSources
- per-instance graphStepActiveContext keying; rebase in instance worktree; clear run-once memo on RETHINK
- GET /api/step-parsers + registry-backed parser select (plugin parsers reachable from editor)
- translate new workflowNodes/workflowFields strings across all 5 non-en locales
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- db.ts: restrict migration-105 orphan-step cleanup to JSON arrays
(json_type guard so json_each can't expand objects/strings)
- project-engine.ts: requestInterpreterMerge throws on null task lookup
instead of casting null into MergeResult (seam converts to clean failure)
- executor.ts: truncate dual-observe shadow stage walk at the live terminal
stage so healthy in-review tasks don't record a phantom merge transition
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migration 102 defers the destructive agentLogEntries drop until TaskStore
copies legacy rows to JSONL and writes the __meta guard, then relies on a
second init() pass gated on schemaVersion < SCHEMA_VERSION. Migrations
103-105 bump the version to 105 on the first pass, so the second pass never
fired and the legacy table survived forever. Make the drop version-independent
in migrate() and trigger the re-init whenever the legacy table remains.
Also pin secrets-schema.test.ts to String(SCHEMA_VERSION) instead of the
hardcoded "102" string the schema bump invalidated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the test-setup git hardening. The engine --shard=2/2 worktree-heavy
reliability suite was hanging in CI (progressing healthily, then ~2 min of
silence before the job timeout killed it with no test failure) — the signature
of a git command blocking on an interactive prompt. A dev macOS git config
suppresses these; a bare Linux CI git does not. Disable terminal credential
prompts (GIT_TERMINAL_PROMPT=0), the editor (GIT_EDITOR=true), and the pager
(GIT_PAGER=cat) for every test process so no git invocation can block on a TTY.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Git defaults the initial branch to 'master' unless init.defaultBranch is set —
true on Linux CI runners but typically overridden to 'main' on developer macOS
machines. That host gap made git-worktree tests assuming 'main' (the
shared-branch-group reliability suite in engine shard 2/2) pass locally but
fail only in CI with 'fatal: path ... does not exist in main'.
Set init.defaultBranch=main for every test process via GIT_CONFIG_* env vars in
the shared core test setup (inherited by all child git invocations, without
mutating the developer's global config). Appends rather than clobbers any
pre-existing GIT_CONFIG_COUNT.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Settings → Experimental gains a 'Workflow Graph Engine — dual-observe parity
(diagnostic)' toggle for the workflowInterpreterDualObserve flag.
- store.getWorkflowParitySummary() aggregates the workflow:parity-observed /
workflow:parity-drift run-audit events into the graduation signal: agree-rate,
per-field drift counts, and recent drift samples. Covered by
workflow-parity-summary.test.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add buildWorkflowObservationFromTask (legacy authoritative side, from a task's
terminal column/status/review/mergeDetails + recorded column history) and
buildWorkflowObservation (interpreter/shadow side, from explicit parts), plus
deriveStageTransitions (maps the task-move column history to execute/review/
merge stages) and DEFAULT_WORKFLOW_INVARIANTS. These let both sides of the
dual-observe seam produce a comparable WorkflowRunObservation without
hand-rolling the shape. Covered by workflow-parity.test.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR advanced @fusion/core's SCHEMA_VERSION 102 → 105 (migrations 103
workflows, 104 task_workflow_selection, 105 orphaned-selection cleanup) but
the "reaches current version after init/migrate" assertions across the core
test suite — and the roadmap plugin's mirror test — still hardcoded 102. The
dashboard build break was masking this: the test shards never ran until the
build was fixed, then all four failed on `expected 105 to be 102`.
Updated every getSchemaVersion()).toBe(102) current-version assertion to 105
(db, db-migrate, goals-schema, insight-store, mission-store, run-audit,
store-merge-queue, merge-request-record, task-documents) plus the roadmap
plugin. agent-log-migration already asserts against the imported SCHEMA_VERSION
constant (the robust pattern); central-db asserts its own version 13 and is
unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the two needs-human findings from PR #1363 review, plus surface the flag.
Merge seam (project-engine.ts):
- manualMergeResolvers is now a per-task LIST of waiters. Both the dashboard
"merge now" path and the interpreter merge seam call onMerge, so a single
resolver per task let the second caller overwrite (and strand) the first.
All resolve/reject/requeue/late-resolver/shutdown sites drain the whole list.
- New requestInterpreterMerge() honors auto-merge eligibility: when autoMerge
is off (or the task isn't merge-ready) it returns merged:false instead of
forcing the merge, so a graph merge node can't override an autoMerge-off
project — it parks the task in review for a human. setMergeRequester now wires
the interpreter to this gate rather than the human bypass.
Selection race (store.ts):
- selectTaskWorkflow/clearTaskWorkflowSelection now hold one withTaskLock across
their whole mutate sequence. Extracted updateTaskUnlocked() (the per-task lock
is non-reentrant, so they couldn't wrap the public updateTask without
deadlocking) and call that inside the lock.
Settings:
- Add "Workflow Graph Engine (run custom workflows)" to the Experimental
Features list so the workflowGraphExecutor flag is a labeled toggle in
Settings → Experimental, not just a raw key.
Tests: interpreter-merge-seam.test.ts (multi-waiter resolve/reject + autoMerge
eligibility gate); existing merge lifecycle/bypass/selection suites still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- needsPrRepair no longer short-circuited by the open-state guard: legacy
fallback rows (finalized + prState open + prNumber null) now repair by
creating the real PR on re-promotion; regression test added
- no-PR abandon route test asserts last persisted call + response body
- goal-provenance fallback test clears missionId on its own in-memory store
so the feature-linkage path is genuinely exercised
- ko added to SUPPORTED_LOCALES and every enumeration site (config,
settings enum, help text, tests); Korean catalogs authored for all
current keys; CLI bundles regenerated for 6 locales; 한국어 endonym
- README translated into zh-CN, zh-TW, fr, es, ko; every README carries
a language-switcher line and the localized ones note that English is
canonical
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>