Skip to content

chore(release): promote dev to main - #142

Merged
LeXwDeX merged 130 commits into
mainfrom
dev
Jul 30, 2026
Merged

chore(release): promote dev to main#142
LeXwDeX merged 130 commits into
mainfrom
dev

Conversation

@LeXwDeX

@LeXwDeX LeXwDeX commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Promote the validated dev integration branch to main for a formal release.

Validation already completed on dev SHA 75953f4:

  • Typecheck passed
  • CodeQL passed
  • Unit tests passed
  • Linux and Windows E2E passed
  • Generated client and SDK checks passed
  • HttpAPI exerciser passed
  • dev prerelease v0.0.0-202607300419 published successfully

This PR intentionally contains the accumulated dev release train.

Test and others added 30 commits July 2, 2026 14:10
…ntime

Phase 0-6 complete:
- Core: pure scheduling logic (graph, layering, transitions, replan, required-validator)
- Core: CQRS read-model (store, projector, sql tables, migration, probe)
- Schema: 15 EventV2 definitions (dag-event.ts) + durable-event-manifest registration
- Opencode: Dag.Service + layer + .node (LayerNode), EventV2Bridge-backed
- Opencode: runtime (scheduling, spawn, eval, recovery, worktree-manager, templates)
- Opencode: workflow tool (start/extend/control)
- HTTP API: DagApi group (6 endpoints) + dagHandlers + server.ts wiring
- TUI: sidebar DAG indicator + full-screen inspector route
- SDK regenerated

Bug fixes applied:
- scheduling: failed nodes no longer counted as 'done' (separate failed Set)
- scheduling: parallel forkDetach for 4 event subscriptions (was serial-blocked)
- handlers: Effect.die -> notFound()/InvalidRequestError (500 -> 404/400)
- spawn: model resolution requires both modelID+providerID (no empty fallback)
- spawn: prompt resolved via resolveTemplate (was hardcoded empty text)
- Removed dead core/dag/dag.ts (duplicate Service tag, zero consumers)

All packages typecheck green. 80 tests pass (49 core + 31 opencode).
Phase 0 (dag-node-completion-semantics, 17/17 tasks complete):
- spawn.ts: replace failure-only catchCause with Effect.gen success path
  that extracts output text and publishes NodeCompleted on prompt resolution
- Fixes F1: successful nodes now publish NodeCompleted, preventing the
  scheduling loop deadlock that froze every workflow after first layer
- 4 new completion tests (success/failure/empty-output/idempotency)

OpenSpec changes (architecture analysis captured as specs):
- dag-node-completion-semantics: completion contract (Level 1 text output)
- dag-scheduler-durability: restart recovery + saga rehydration (Phase 2)
- dag-scheduling-correctness: 6 internal bugs (race/replan/pause/cleanup)
  (Phase 2)
- dag-structured-output: Level 2 data flow (structured output/input_mapping)
  (Phase 2)
- dag-runtime-wiring spec relaxed: permits spawn.ts completion bridge
- dag-runtime-wiring proposal: marks completion-semantics as prerequisite

Boundary: Level 1 output is plain text; input_mapping field references
(nodeID.output.field) resolve to undefined until Level 2 lands.
Three improvements for offline / restricted-network startup that are not
covered by the already-merged offline-catalog PRs (#88-#91):

1. Plugin SDK offline bundling: build.ts copies the plugin source into a
   vendored directory next to each binary. At runtime npm.install resolves
   @opencode-ai/plugin via three-tier fallback — project-local > bundled
   copy > registry — so plugin installs succeed without network access.

2. dependencyVersion(): only pin the version on stable latest releases
   (channel=latest + pure semver x.y.z). Fork/prerelease/local builds get
   undefined so the install doesn't request a non-existent registry version.

3. Reference offline degradation: only register a Git reference when its
   cache directory actually exists, and update the entry after a successful
   refresh. Missing references are omitted instead of carrying a stale path.
fix(core): bundle plugin SDK and degrade references for offline startup
…aboration patterns

Move strategy guidance out of the tool description into a built-in skill
loaded on demand. The tool description (always in context) is now a lean
syntax reference; the skill body (loaded when needed) carries:

- Orchestration lifecycle: 5-phase meta-workflow (explore+brainstorm →
  review gate → parallel execution → audit+merge → expansion decision)
  with worked YAML examples and iterate/extend/complete exit branches
- Four collaboration patterns: staged pipeline with gate, parallel
  fan-out, adversarial multi-model review, diverge-converge brainstorm
- Adaptive replanning, model assignment strategy, design principles

Skill description includes trigger keywords (动态工作流, dynamic workflow,
workflow) so the agent loads it when the user describes heavy orchestration
tasks.
Correctness fixes:
- Serialize terminal-event re-evaluation via per-workflow Semaphore(1)
  (evalLock) across all event handlers, closing the double-spawn race
- Isolate InvalidTransitionError in spawnNode so guard failures on
  nodeCompleted/nodeFailed don't route into catchCause and deadlock
  the node with zero terminal events
- Add RUNNING → SKIPPED to node transition table so nodeCancelled
  succeeds from running state
- Broaden reconcileWorkflow to detect pending orphans (crash between
  session create and NodeStarted), with retroactive NodeStarted
- Fix structured-output gaps: log warning on invalid JSON, validate
  parsed output against declared schema, interpolate {{var}} before
  appending context block
- Replace try/catch + JSON.parse with Schema.UnknownFromJsonString

Standards fixes:
- Replace for-loops with functional methods in scheduling.ts
- Replace if/else-if cascade with early returns in dag handler
- Migrate test stubs from Service.of({...}) to Layer.mock
- Replace Effect.sleep sync with Fiber.await in tests
- Export and share SUCCESS_TERMINAL/toSchedulingNodes + fixtures

Verification: typecheck clean (core + opencode), 133 tests pass.
Interactive terminal tool that:
- Discovers and merges all opencode config layers (managed/inline/project/custom/global)
- Generates provider config blocks from models.dev metadata
- Validates provider/model consistency against enabled/disabled policies
- Writes atomically with backup, file locking, and 0600 permissions

Covers full config discovery (OPENCODE_CONFIG_DIR, .opencode dirs, multi-platform
managed paths, macOS MDM plist), JSONC parsing with trailing commas, sensitive
field redaction in TUI, cross-provider model mapping, schema-required limit/cost
fields, Windows MoveFileEx atomic replace, and HOME-failure fail-closed.
feat(config): add config_assistant Go TUI for opencode config management
Persists the merged workflow config as source of truth after replan
(WorkflowConfigUpdated event) so node definitions survive restarts,
and re-attaches completion watchers for running nodes on recovery so
crashed-and-restarted workflows don't leave nodes stuck in running.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Deadline-bounded node execution with semaphore-aware timeout; durable
wake/replan columns and migration; report_to_parent node-level wake
delivery with per-session serialization and crash-recovery drain; replan
ceiling enforcement (per-node + live-node-count total); crash recovery
that preserves pending nodes and cancels stale child sessions; workflow
terminal cleanup that cancels all active children under evalLock.

Key correctness fixes across multiple review rounds:
- execute semaphore.release as Effect in both spawn and recovery watchers
- D7 orchestrator-unresponsive scoped to delivered dagIDs only
- NodeSkipped/NodeCancelled projector status guards
- workflow cancel/complete/fail publishes terminal node events
- replan total-ceiling check moved before any event publication
- /goal deprecation regex word boundary fix

Verified: core + opencode typecheck, 170/170 DAG tests, git diff --check.
…ix cancel cascade

- Remove goal module entirely (goal/, tool/goal.ts, GoalLoop, goal HTTP API,
  goal TUI sidebar). /goal and /subgoal route to /workflow with deprecation notice
- Replace text-parse structured output with submit_result tool: child agent calls
  submit_result, payload validated against output_schema and persisted to
  workflow_node.captured_output column (crash-safe across restarts)
- Fix C1: NodeCancelled now projects to failed status and triggers markUnsatisfied
  cascade, so cancelled nodes' downstream dependents are auto-failed (was inverted
  to markSatisfied, letting dependents run without input)
- Fix C1: skip spawnReady during replan event batch; WorkflowReplanned handler
  does the single post-batch spawn
- Fix C2: captured payload persisted to DB (captured_output column) instead of
  in-memory Map; all three completion paths (fresh spawn, recovery watcher,
  reconcile) read from node row and agree on verdict_fail for schema nodes
  without captured output
- Fix C3: max_total_nodes counts all nodes ever registered (cumulative lifetime)
  instead of only non-terminal nodes, preventing cancel-N-add-N churn from
  bypassing the ceiling
- Clean phantom config fields (use_worktree, retry, replan_policy, report_strategy)
- Change condition evaluation to fail-closed (unparseable → NodeFailed)
- Default max_concurrency 4 → 5
- Wake messages include act-now obligation text for node-level wakes
- Add budget documentation to workflow.md and skill workflow.md
- Regenerate JS SDK after route changes
- Sync delta specs to main specs, archive change simplify-dag-retire-goal
Field cleanup:
- Remove unused WorkflowConfig.timeout_ms and .description
- Make max_concurrency optional (aligns type with runtime ?? 5 fallback)
- Sync WorkflowGraphSchema in workflow.ts

Dead code/column removal:
- Delete probe.ts (4 functions, zero consumers) and core/layering.ts
- Remove dead retry_count column + add DROP COLUMN migration

Correctness fixes:
- create() now validates the full graph for cycles (not just required nodes)
- NodeCompleted projector gains WHERE status guard (pending/queued/running)
  preventing stale events from flipping terminal nodes

Watcher removal:
- Delete attachNodeCompletionWatcher, runPollLoop, attachAbandonedSessionWatcher
- Crash recovery now relies solely on reconcileWorkflow one-time scan
- Stale comments in recovery.ts and loop.ts corrected

Document truth source unified (BREAKING):
- Delete tool/workflow.md; description now references SkillPlugin.WorkflowContent
- Add .annotate({description}) to all workflow tool schema fields
- Append Tool Reference section to skill workflow.md

Trigger path unified (BREAKING):
- Remove /workflow, /goal, /subgoal command registrations
- Remove prompt.ts special-case dispatch branches

Tests:
- Remove 8 watcher-related tests
- Add cycle detection, terminal irrevocability, projection guard tests
- Make extend operation additive to preserve pending/queued/paused nodes
- Fix replan edge-building to keep original deps for running nodes without restart
- Guard NodeCompleted/Skipped and NodeFailed handlers against stale events
- Add path traversal protection to template ID resolution
- Improve nodeCancelled validation and remove dead code
- Update projector status guards to include paused state
On a replan-restart (NodeRestarted → NodeStarted with a new child
session), captured_output from the dead prior run survived, defeating
the verdict_fail completion path in both consumers (spawn.ts in-process
and recovery.ts crash-recovery): a restarted node that never re-called
submit_result would silently complete with the previous run's payload.

Add captured_output: null to the NodeStarted projector patch alongside
the existing per-attempt resets (child_session_id, started_at, etc.),
matching the dag-structured-output spec requirement. Late
setCapturedOutput(oldSession) writes are no-ops after NodeStarted
(newSession) since they key on child_session_id.

Adds a real-layer integration regression test (DB + EventV2 + Projector
+ Store) that fails without the fix. Also clarifies the 'survives
restart' comments to distinguish process-crash survival from
replan-restart reset.
feat(dag): DAG workflow engine with runtime safety hardening
chore(opencode): add config.json schema stub
…rompt injection

- projector: guard NodeStarted against terminal statuses (stale event resurrection)
- loop/recovery: fail orphaned nodes on expired deadline; safety net uses active-fiber-aware hasRunningMatching so post-crash all-orphaned workflows no longer hang
- spawn: catch terminalization race during spawn window, cancel orphaned child session, return no-op fiber (no spurious NodeFailed)
- templates/sanitize: recursive sanitizeInput on resolvedMapping closes injection gap on dynamic input_mapping
- types: FAILED now returns [] in transition table, aligning with isNodeTerminalStatus (iron law #2) — restart path uses RUNNING->PENDING, never FAILED
- tests: cover NodeStarted guard, recovery deadlines, spawn terminalization, hasRunningMatching, nested sanitize
fix(dag): harden runtime against stale events, orphaned fibers, and prompt injection
Two dev-branch CI failures in Unit Tests (linux):

1. 'declared schema has no ungenerated migrations' — the dynamic-workflow
   schema changes (workflow_node.captured_output column, dropped goal_state
   table) were committed without running the migration generator. Generate
   the migration via `bun script/migration.ts` and refresh schema.json,
   migration.gen.ts, schema.gen.ts.

2. 'uses configured provider env vars as credentials' timed out at 5000ms —
   the test registers an env credential, which makes OpencodePlugin.load()
   issue a real HTTPS request to console.opencode.ai via fetchProviders.
   Network availability determined pass/fail. Stub the HttpClient to return
   404 (no remote config) so the test stays on local defaults and never hits
   the network; the connected-server it.live test keeps the real client.
Stateless DagSummaryPublisher recomputes per-session WorkflowSummary[] from
DagStore on every dag.* event and pushes an ephemeral
dag.workflow.summary.updated event via GlobalBus. Adds the
/dag/session/:sessionID/summary route, DagStore.getWorkflowSummaries, the
WorkflowSummary schema, regenerated SDK, the TUI sync store slice with a
bootstrap fallback fetch, and reuses the SDK type (DagWorkflowSummary) in
plugin/tui.ts instead of a hand-duplicated interface. Control ops that fail
with InvalidTransitionError/TerminalViolationError now map to 409 Conflict.
Add keyboard navigation (j/k node, h/l workflow, enter child session,
escape/q close) and workflow control (p pause / r resume / x cancel) bound
via useBindings with new dag_* keybind definitions. Auto-selects the first
workflow/node and re-validates selection as data changes; guards stale fetch
responses and cleans up event subscriptions with onCleanup. Extract the
topological wave layout into dag-inspector-utils (computeWaves) with unit
tests; missing dependencies from a replan are now treated as satisfied.
SyncProvider calls useExit() since #31524, but the sync test harness did not
mount ExitProvider, causing 8 pre-existing test failures (sync, live
hydration, undefined messages). Wrap the fixture mount with ExitProvider.
Add a TUI (packages/tui) section under Extending the Codebase covering:
builtin registration, route-scoped keybinds plus Definitions/CommandMap
config tables, the sync store slice with bootstrap fallback fetch and the
ExitProvider dependency, ephemeral vs durable events, SDK type reuse,
server-side aggregation with stateless publishers, and *-utils extraction.
fix(core): generate pending db migration and stub network in plugin test
feat(dag): complete workflow summary pipeline and TUI surface
LeXwDeX and others added 28 commits July 28, 2026 21:31
…iscovery fails

bun's ancestor-directory workspace walk dies with CouldntReadCurrentDirectory in sandboxed checkouts, failing hooks on an environment defect. The shared runner keeps 'bun typecheck' as the primary path and falls back to running each package's own typecheck script only on that specific error; real type errors still fail on either path.
…fallback

tsgo -b packages (app/desktop) can fail with TS5033 'operation not permitted' writing .tsbuildinfo under sandboxed hook contexts. Tolerate a package failure only when TS5033 is the sole TS diagnostic; real type errors still fail.
fix(dag): cross-instance adoption guard, subscription defect survival, cancel-skip race
…osal contract

Restructure the dag-flow orchestration manual around a tiered doctrine: advanced-tier nodes conduct and check, standard-tier nodes buy accuracy with breadth (concurrent fan-out) and depth (verdict-gated waves). Add depth ladder hard minimums, graded review profile with claim verification, verdict disposal contract at the terminal boundary, ship the 12 dag-prompts templates the manual promised, fix the arbiter dead-end example, and drop the dead dagworker template relic.
feat(core): tiered orchestration doctrine, depth ladder, verdict disposal contract
Loop handlers for NodeCompleted/NodeSkipped/NodeFailed now confirm the node row still reflects the event's terminal status before touching fibers or runtime state, so events from a previous generation (after NodeRestarted/replan) are dropped. Schema capture validation additionally enforces const/enum via structural equality.
fix(core): drop stale dag node terminal events via db status cross-check
chore(ci): enforce oxlint warning ratchet in CI and pre-commit
fix(tui): dag inspector frame alignment, padding, and wave grouping
chore: rename project to OpenCode-GraphAgent
Replaces the diff-viewer frame vocabulary with the chat transcript's: rail
and panel blocks instead of box-drawing lines, two-column side padding, and
a fixed 42-wide sidebar gated on the same > 120 terminal width chat uses.

- Responsive sidebar: below 120 columns the sidebar squeezed node names into
  4-character truncation, so it is dropped and the summary block carries the
  workflow position instead.
- Selection now uses primary + selectedForeground() for the node cursor and
  backgroundElement for the secondary workflow selection, matching the select
  dialog's focused/unfocused semantics. backgroundElement alone meant
  "selected but unfocused" and read as an 8% luminance nudge.
- Cursor keys only: down/up move nodes, left/right switch workflows, return
  opens, escape closes. j/k/h/l/tab/q are gone and pause/resume/step/cancel
  are unbound by default, reachable through the command palette instead, so
  the footer stays a single uncrowded row.
- Tightens the lint ratchet to 4690: a runCommand test helper collapses ten
  duplicated command-context assertions into one.
fix(tui): rebuild dag inspector on the chat design language
…cabulary

The previous revision transplanted chat's structure instead of learning its
design language. Three chat-specific structures were imported and are now
removed:

- The 42-wide right sidebar. In chat that container holds ambient session
  metadata; here it held the workflow list, which is primary navigation.
- The > 120 width gate that came with it. A threshold designed for optional
  metadata made primary navigation vanish on narrow terminals; the earlier
  "responsive" fix copied chat's breakpoint and entrenched the mistake
  instead of questioning the transplant.
- The SplitBorder rail-and-panel block. In chat that marks one utterance in a
  stream; the workflow summary is persistent state, not an utterance.

Structure now comes from what this screen actually is, a full-screen
inspector: title row, always-present navigation pane, content pane, detail
pane, footer. Navigation width is derived from the pane's own needs,
clamp(18, 32, width * 0.3), so it narrows instead of disappearing.

Only the vocabulary is borrowed: regions are marked by the panel surface plus
a padding rhythm rather than drawn box characters (which also settles the
original misaligned-rule complaint), token semantics for text/textMuted/
backgroundElement/primary, and bold reserved for identity and section
headers.

The width regression test now asserts the inverse invariant: the workflow
list is present at 130, 100 and 70 columns.
fix(tui): restore the dag inspector's own structure, keep only the vocabulary
fix(dag): harden workflow startup controls
fix(dag): harden workflow spec handoffs
fix(dag): harden recovery and replan races
@LeXwDeX
LeXwDeX enabled auto-merge July 30, 2026 04:46
@LeXwDeX
LeXwDeX merged commit 0b018e5 into main Jul 30, 2026
22 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant