@op1/threads 0.1.7 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,32 @@
1
1
  # @op1/threads
2
2
 
3
- Managed top-level worker sessions for OpenCode 2.0.7. The server entrypoint is `index.ts`; the terminal entrypoint is `tui.ts`.
3
+ Managed top-level worker sessions and dynamic workflows for OpenCode V2. Dynamic workflows target OpenCode 2.0.12. The server entrypoint is `index.ts`; the terminal entrypoint is `tui.ts`.
4
4
 
5
- ## Install
5
+ ## Dynamic workflows
6
6
 
7
- Install the plugin globally:
7
+ Use `/workflow-run <task>` to have OpenCode author a JavaScript workflow with parallel agents, structured handoffs, and a durable run journal. `/workflows` opens the run navigator. Workers use native OpenCode conversations and configured agent profiles, including VERA roles.
8
+
9
+ Dynamic workflows are included in `@op1/threads` 0.2.0. The `v0.1.8` tag preserves the pre-workflow release.
10
+
11
+ Read [Run a dynamic workflow](docs/workflows.md) for progress, pause, stop, resume, worktree, and saved-script usage. The [runtime contract](skills/workflow-authoring/references/runtime.md) documents the authoring API and execution limits.
12
+
13
+ See [verification evidence](docs/workflows-verification.md) for the native checks and recovery guarantees.
14
+
15
+ See [capacity and limits](docs/workflow-capacity-findings.md) for total agent steps, concurrent workers, and measured load.
16
+
17
+ ## Install dynamic workflows
18
+
19
+ Install the versioned plugin globally:
8
20
 
9
21
  ```sh
10
- opencode plugin add @op1/threads
22
+ opencode plugin add @op1/threads@0.2.0
11
23
  ```
12
24
 
13
25
  Or add it to `plugins` in `~/.config/opencode/opencode.jsonc`:
14
26
 
15
27
  ```jsonc
16
28
  {
17
- "plugins": ["@op1/threads"]
29
+ "plugins": ["@op1/threads@0.2.0"]
18
30
  }
19
31
  ```
20
32
 
@@ -22,7 +34,7 @@ Keep the other entries in your plugin list. Open a fresh TUI to load the termina
22
34
 
23
35
  The `skills/managed-sessions` directory contains the VERA delegation guide. Copy or link it into `~/.config/opencode/skills/managed-sessions` to make it available to agents.
24
36
 
25
- For local development, clone [opzero1/threads](https://github.com/opzero1/threads), run `bun install`, and use the clone's absolute path as the plugin entry instead. The internal plugin ID remains `op-threads`, so switching between local and published installs preserves worker records.
37
+ The internal plugin ID remains `op-threads`, so switching between local and published installs preserves worker records. For local development, clone the `dynamic-workflows` branch, run `bun install`, and use the checkout's absolute path as the plugin entry.
26
38
 
27
39
  ## Delegate work
28
40
 
@@ -88,11 +100,17 @@ Send keys are scoped to the worker and determine a stable message ID. A retry wi
88
100
 
89
101
  Plugin option `maxWorkers` defaults to 4 and accepts integers from 1 through 32. Admission is serialized by coordinator within the loaded server process. A worker without a report continues to occupy a slot unless its native outcome is `failed` or `interrupted`. A successful run without a report does not silently free its slot.
90
102
 
103
+ Plugin option `workflowConcurrency` sets the default concurrency for new workflows. It accepts integers from 1 through 8 and defaults to 3. Plugin option `workflowMaxAgents` sets their total agent-step limit, accepts integers from 1 through 1,000, and defaults to 4. Explicit `workflows_start` values override these defaults. Existing runs retain their recorded limits. For eight concurrent agents with eight total steps, configure `"options": { "maxWorkers": 32, "workflowConcurrency": 8, "workflowMaxAgents": 8 }` on the plugin entry.
104
+
91
105
  ## Terminal and RPC
92
106
 
93
- The terminal synchronizes workers before opening native tabs without changing focus. A TUI memory index survives plugin reloads and respects manually closed tabs. `/threads` explicitly reopens workers for open coordinator tabs. A new TUI recovers workers from durable storage. Closing the TUI does not interrupt workers.
107
+ The terminal synchronizes workers before opening native tabs without changing focus. A TUI memory index survives plugin reloads and respects manually closed native tabs. Conversations closed through Activity stay dismissed across restarts. `/threads` explicitly reopens workers for open coordinator tabs, including workers dismissed through Activity. A new TUI recovers the other workers from durable storage. Closing the TUI does not interrupt workers.
108
+
109
+ Activity shows Main and Worker roles as row subtitles. It omits a leading `[Main] ` or `[Worker] ` from known managed titles, including closed history rows and prefixes reapplied by older clients. Managed identity comes from the worker RPC, including lookups for loaded history conversations whose workers are on older pages. Unrelated prefixed titles remain unchanged.
110
+
111
+ For open managed tabs, the TUI also removes one legacy prefix from the fresh saved title. The remainder stays unchanged, and empty remainders are not saved. A durable client-storage marker makes this saved-title cleanup one-shot per session. Later intentional prefixed renames remain in saved history, while Activity omits the redundant role label from its display.
94
112
 
95
- Managed tabs use saved title prefixes: `[Main]` for the coordinator and `[Worker]` for each managed worker. Labels appear when the TUI discovers the relationship, including existing threads. The rest of the title stays editable; renaming a managed conversation reapplies its role prefix. Unrelated sessions keep their titles. OpenCode's native tab API has no separate badge field, so the prefixes also appear in session history.
113
+ When upgrading, restart all existing TUIs. Older 0.1.7 clients still write role prefixes and can reapply them after cleanup.
96
114
 
97
115
  Workers with `PASS` or `PASS WITH NOTES` reports hide automatically once idle. This also applies to reports saved before upgrading. Unreported workers and `FAIL` or `INCONCLUSIVE` reports stay visible. The selected tab, running workers, and tabs needing input stay open until they are inactive.
98
116
 
@@ -100,7 +118,7 @@ The coordinator can call `threads_hide` when a worker is no longer needed. Hidin
100
118
 
101
119
  Reports reach the coordinator through silent synthetic messages. They remain available through `threads_list` and the session history without adding a notification row to the conversation. Older report notification rows remain in native history.
102
120
 
103
- All open native root-session tabs are automatically grouped by OpenCode project ID, including sessions not managed by this plugin. Projects follow their first appearance in the current tab order. Worktrees with the same project ID stay together.
121
+ With Activity disabled or unable to mount, open native root-session tabs are grouped by OpenCode project ID, including sessions not managed by this plugin. This includes horizontal, narrow, and unsupported-version fallback. Projects follow their first appearance in the current tab order. Worktrees with the same project ID stay together.
104
122
 
105
123
  Within each project, running sessions and sessions waiting for input come before idle sessions. Tabs with the same priority keep their relative order. Selecting an idle tab does not reorder it. Native tab order is shared between terminals in the same directory, so sorting by each terminal's selection would make them repeatedly undo each other's moves. Idle is a display priority, not a completion verdict. Each tab with unloaded project metadata stays in its own group until that metadata becomes available. Reconciliation moves only out-of-order tabs, without changing focus or closing and reopening them to reorder. Native tabs do not support divider rows.
106
124
 
@@ -113,14 +131,74 @@ result: { workers: WorkerView[] }
113
131
 
114
132
  The input accepts at most 100 coordinator IDs. Raw HTTP RPC requests wrap the input as `{ "input": { "coordinatorIDs": ["ses_..."] } }`. Each method declares `errors: {}` and the RPC declares `events: {}`. The TUI subscribes to native session events and reconciles at most one snapshot at a time, with a three-second missed-event refresh.
115
133
 
134
+ ## Activity sidebar
135
+
136
+ Activity replaces the contents of the native left vertical tab rail on compatible OpenCode V2 layouts. Native tabs remain enabled and retain their layout reservation, resize handle, activity state, and session navigation. The prompt and transcript remain native OpenCode UI.
137
+
138
+ Subtitles start with the project name or folder basename, followed by a distinct location or worktree basename when needed. Main and Worker labels appear only for known managed relationships; ordinary history rows show project and location without a role label.
139
+
140
+ Rows are grouped in this order:
141
+
142
+ - **Priority**: permission or form attention, then running work, then unread results or errors.
143
+ - **Pinned**: pinned conversations without priority activity.
144
+ - **Today**, **Yesterday**, recent weekdays, and older calendar dates, using the local timezone.
145
+
146
+ Divider lines separate sections. Click a heading to collapse or expand its conversations; the arrow and count show its current state. Collapsed sections stay collapsed across reloads and restarts. This changes sidebar visibility without closing tabs, and `/activities` can still find conversations in collapsed sections.
147
+
148
+ A session appears once. Priority takes precedence over pinning. Idle does not mean that a task passed. Open-tab status comes from the native tab API; closed history uses available native running and input-request state, without inventing a success status. Date groups use the last native execution's `time.idle`, falling back to `time.updated` for conversations without an execution. Title cleanup therefore does not move an older executed conversation into Today.
149
+
150
+ Running rows use OpenCode's registered animated dot spinner. Input requests take precedence over the spinner, and it is removed when the session becomes idle.
151
+
152
+ Colors follow the active theme: selected conversations and date headings use its accent, running indicators use its information color, and input requests use its warning color. Unread errors use the error color. Worker headings and pinned controls use purple, while project subtitles and inactive controls use subdued text. Blank lines separate section headings from conversations and give worker groups space above and below their heading. Long titles are truncated to keep the action buttons separate.
153
+
154
+ Managed workers appear indented under their main conversation. The stack appears in **Priority** when any member is running, needs input, or has an unread result. Otherwise, a pin on any visible member places the stack in **Pinned**; date sections use the most recent member activity. Workers with an unavailable main conversation remain standalone rows.
155
+
156
+ Each main conversation with workers has a **▾ Workers (N)** heading below its subtitle. Click the heading to collapse the worker list, or **▸ Workers (N)** to expand it. When the list is collapsed, the main row summarizes worker activity, with input requests taking precedence over running spinners. Collapsing keeps the workers running and their tabs open. Stack preferences survive reloads and restarts, and `/activities` still lists collapsed workers.
157
+
158
+ Activity fetches the 100 most recent root sessions across projects and refreshes them in the background. Open and pinned sessions are merged into the list; older pins and known main conversations are resolved individually. Archived, deleted, and native child sessions are excluded. Closed managed workers are not reintroduced by fetched history. `/threads` restores their native tabs using the existing worker visibility rules.
159
+
160
+ Left-click a row to open or focus its real session. Each row has one-click **[◇]** (pin), **[◆]** (unpin), and **[×]** (close from Activity) buttons beside its title. These controls work on both main and worker rows without selecting a background conversation. Closing removes the row and closes its native tab when open. Saved history and the pin preference remain intact. Dismissed rows stay hidden through refreshes, reloads, and restarts. Selecting a closed conversation in `/activities` restores it; `/threads` also restores dismissed managed workers. Reopening a conversation through native session navigation restores its Activity row. Right-click also opens the action menu. The mouse wheel scrolls the list, and the native rail edge remains draggable.
161
+
162
+ | Command | Action |
163
+ | --- | --- |
164
+ | `/activity` | Toggle Activity and the native rail |
165
+ | `/pin` | Pin or unpin the current conversation |
166
+ | `/activities` | Choose a loaded conversation, including closed Activity rows, and restore it if needed |
167
+ | `/activity-sections` | Choose a section to collapse or expand |
168
+ | `/activity-threads` | Choose a managed worker stack to collapse or expand |
169
+ | `/threads` | Restore managed worker tabs |
170
+
171
+ The same actions are available in the command palette. **+ New session** dispatches OpenCode's registered `session.new` command. Pins use plugin client storage and survive TUI reloads and restarts.
172
+
173
+ In `/activities`, **Ctrl+F** pins or unpins the highlighted conversation. The picker stays open and preserves your search and selection. This also works for conversations in **Priority**. **Enter** opens the highlighted conversation, and **Esc** closes the picker. The footer shows the current pin action. Configure `threads.activity.choose.pin` in `cli.json` to change the shortcut.
174
+
175
+ Activity requires vertical tabs. To select the layout and disable Activity by default, configure the terminal plugin in `~/.config/opencode/cli.json`:
176
+
177
+ ```json
178
+ {
179
+ "tabs": { "layout": "vertical" },
180
+ "plugins": [{ "package": "@op1/threads", "options": { "activity": false } }]
181
+ }
182
+ ```
183
+
184
+ Omit `activity` or set it to `true` to enable the sidebar. Keep the server plugin in `opencode.json`; server plugin options are not forwarded to the terminal entrypoint. For local development, replace the package name in both files with the clone's absolute path.
185
+
186
+ The left rail has no public plugin slot in 2.0.7. `src/activity-rail.ts` is an internal compatibility adapter: it checks a bounded render-tree structure and unambiguous geometry, hides native children while remembering their visibility, and mounts only plugin-owned content. OpenTUI constructors are imported at the terminal entrypoint to preserve host identity. The adapter uses renderer pre-paint callbacks and resize events, not continuous tree polling or root monkeypatches. It restores native children on detach, toggle, and disposal. Compatibility depends on the detected rail structure and geometry, regardless of the OpenCode version string. Unrecognized rail structures, horizontal layouts, and narrow layouts fall back to native tabs. Activity never moves shared native tabs while mounted; native project and activity grouping resumes during fallback.
187
+
188
+ `src/activity-theme.ts` accepts both `base`/`muted` and legacy `default`/`subdued` theme tokens. Missing colors use a valid fallback before reaching the renderer: assigning an undefined spinner color aborts the entire frame. Accent and worker colors follow the theme's foreground brightness rather than fixed light/dark shade numbers. If a hue lacks sufficient text contrast, Activity uses the theme's normal foreground. The `@opencode/theme` development dependency keeps the host theme types available to TypeScript. Renderer regression tests cover both token shapes and missing colors, including spinner construction and subsequent color updates.
189
+
116
190
  ## Verification and limits
117
191
 
118
- Run `bun run typecheck`, `bun test`, and `bun run verify:live`. The live check requires OpenCode 2.0.7, Python, and `uv`. It starts a separate local server, a deterministic model endpoint, and a terminal process with isolated configuration and data. It verifies actual tool calls, durable messages, permission restrictions, worker limits, deleted-worker cleanup, restart behavior, and native tab visibility, busy state, and focus.
192
+ Run `bun run typecheck`, `bun test`, and `bun run verify:live`. The live check requires OpenCode V2, Python, and `uv`. It starts a separate local server, a deterministic model endpoint, and a terminal process with isolated configuration and data. It verifies actual tool calls, durable messages, permission restrictions, worker limits, deleted-worker cleanup, restart behavior, and native tab visibility, busy state, and focus.
119
193
 
120
194
  Run `bun run verify:tabs` to verify project grouping across real git worktrees, activity-based ordering, permission prompts, completed and resumed workers, focus preservation, and TUI reopening.
121
195
 
196
+ Run `bun run verify:activity` to exercise Activity enabled against an isolated instance of the installed OpenCode version. The suite clears its own `.audit/activity` artifacts, uses the deterministic model fixture, and reads actual renderer bounds through the test-only TUI probe. It checks layout, click and keyboard navigation, one-click pin and close controls, and native prompt responses. It also covers section and worker-stack collapse, aggregated worker status, title cleanup, worker restoration, fallback layouts, reloads, and restart persistence. `verify:tabs` and `verify:idle-tabs` explicitly disable Activity to inspect native ordering.
197
+
122
198
  Run `bun run verify:roles` to verify named profiles against the native server and deterministic model endpoint. It checks actual system prompts, model variants, native delegation, read-only execution, reporting, and role-aware retries.
123
199
 
200
+ Run `bun run verify:workflows` to exercise dynamic scripts through real OpenCode tools and sessions. The isolated fixture checks structured pipelines, exact retries, role restrictions, ownership, checkpoints, saved scripts, retained worktrees, service restart, and the terminal navigator.
201
+
124
202
  Run `bun run verify:idle-tabs` to open two native terminals on different idle sessions in the same directory and verify that their shared tab order stays stable.
125
203
 
126
204
  Pass an extracted package directory to test the release artifact: `bun run verify:live /absolute/path/to/package`.
@@ -0,0 +1,46 @@
1
+ # Dynamic workflows implementation
2
+
3
+ ## Completion criteria
4
+
5
+ An OpenCode V2 session can submit a JavaScript workflow, continue its conversation while agents run, inspect phases and worker sessions, receive validated results, pause or stop, and resume recorded work after a service restart. Writing steps can use retained worktrees. Saved workflows can run again with different arguments. VERA profiles retain their configured models and permissions. A failed or inconclusive task cannot silently become a passing workflow.
6
+
7
+ Verification covers the confined interpreter, the durable runner, actual OpenCode session and tool calls, restart recovery, and the terminal controls. A final real-model run exercises workflow authoring and structured handoffs. An independent reviewer inspects the implementation and evidence before completion.
8
+
9
+ ## Baseline
10
+
11
+ - Threads: `df7686af1c6601ae7d05ae607bc584f185109231`, annotated tag `v0.1.8`, already present at `opzero1/threads`.
12
+ - Dotfiles: snapshot the current tracked setup and intended additions in `afif-reap/dotfiles` before changing VERA guidance.
13
+ - Installed OpenCode: `2.0.12`.
14
+
15
+ ## Ownership
16
+
17
+ Implement workflow modules in the existing Threads package. This shares the worker service and its ownership checks without requiring an unauthenticated cross-plugin dispatch API. The `workflows` tool namespace and RPC remain distinct from the existing `threads` interface.
18
+
19
+ VERA selects the process and owns the final engineering verdict. The workflow runner schedules steps, validates handoffs, journals progress, and reconciles interrupted work. OpenCode owns model execution, permissions, tools, and worktrees. The TUI renders server state and links to native sessions.
20
+
21
+ Use the published `@opencode/codemode` package pinned to `2.0.12` for confined execution. The initial reference checkout marked its older package private; the published V2 package has a supported export. Generated scripts never run through host `eval`, `Function`, or Node `vm`.
22
+
23
+ ## Protocol
24
+
25
+ 1. Checkpoint the repositories and capture the existing test baseline.
26
+ 2. Prove the confined script adapter with real interpreter tests: structured fan-out, per-item pipelines, deterministic inputs, cancellation, invalid scripts, and failures.
27
+ 3. Implement the durable runner and worker adapter. Persist dispatch identities before starting work. Record validated results before returning them to scripts. Reconcile existing worker sessions on resume. Reject concurrent execution of the same run.
28
+ 4. Integrate role authorization, structured result reporting, retained worktrees, concurrency and call limits, measured usage, saved workflows, and bounded retry helpers.
29
+ 5. Add workflow tools, server commands, RPC, terminal navigation, phase and step progress, and pause, stop, resume, and checkpoint controls.
30
+ 6. Add the workflow-authoring and VERA recipes. Verify actual OpenCode sessions and terminal interactions in isolated fixtures, then a scoped real-model run.
31
+ 7. Inspect the complete diff, independently audit behavior and the decision trail, and resolve accepted findings.
32
+
33
+ ## Recovery contract
34
+
35
+ The journal records a run, its script and arguments, each named step's request fingerprint, worker identity, execution outcome, validated result, and evidence. A restart does not blindly replay user-visible effects. Resume first reconciles an existing worker and any recorded result. An interrupted write with uncertain state requires inspection rather than an automatic fresh worker.
36
+
37
+ Script control flow uses only arguments and recorded step results. Reject clock and randomness access. A run's script and arguments are immutable. Resume reuses matching named steps; a changed request under the same key fails closed. An edited saved script starts a new run. This avoids reusing results whose dependencies changed through untracked script control flow. Worktree paths and results survive completion. Integration is a distinct verified action.
38
+
39
+ ## Reference guidance
40
+
41
+ - [Pi Dynamic Workflows](https://github.com/QuintinShaw/pi-dynamic-workflows): code orchestration, role routing, journaling, worktrees, and interactive progress.
42
+ - [Devin Dynamic Workflows](https://docs.devin.ai/work-with-devin/dynamic-workflows): structured per-item pipelines and when workflows are useful.
43
+ - [Claude Code workflows](https://code.claude.com/docs/en/workflows): deterministic scripts, replay semantics, saved commands, background controls, and validation before spawning.
44
+ - [OpenCode V2 plugins](https://opencode.ai/v2/docs/build/plugins): supported host integration.
45
+
46
+ The references inform the behavior. Existing OpenCode and VERA ownership, permissions, and evidence requirements determine the implementation.
@@ -0,0 +1,47 @@
1
+ # Workflow capacity findings
2
+
3
+ The native load harness uses an isolated OpenCode service and a deterministic local provider. It makes no paid model calls. The package uses `@opencode/plugin` and `@opencode/schema` 2.0.7, with `@opencode/codemode` 2.0.12.
4
+
5
+ ## Exact bounds
6
+
7
+ - Workflow concurrency defaults to 3 and accepts 1–8. The plugin option `workflowConcurrency` overrides the default for new runs; explicit run values take precedence.
8
+ - A workflow defaults to 4 agents and accepts 1–1,000 total agent steps. The plugin option `workflowMaxAgents` overrides the default for new runs; explicit run values take precedence.
9
+ - Worker and run timeouts accept 1 second through 7 days. Defaults are 30 minutes per worker and 24 hours per run.
10
+ - Threads `maxWorkers` defaults to 4 and accepts 1–32. It is both the unfinished managed-worker admission limit and the owner-wide workflow execution limit. A run's effective concurrency is therefore `min(concurrency, maxWorkers)`, with `maxWorkers` shared by simultaneous workflows owned by one session.
11
+ - Workflow workers are top-level native OpenCode sessions. Workflow workers cannot delegate, start workflows, or spawn managed workers. Managed Threads workers may use native subagents if their permissions allow it, but cannot spawn another managed worker.
12
+ - Saved workflow nesting permits four `workflow()` boundaries; the fifth fails. Cumulative host calls are bounded at `maxAgents * 8 + 100`, shared with nested calls. Arguments, host-call payloads, results, checkpoint responses, and each durable worker report are limited to 1 MiB.
13
+ - The runtime module admits 64 simultaneous interpreter calls, including nested calls. These Bun workers execute scripts; they are separate from native agent sessions. Excess calls fail immediately rather than waiting for nested work to free a slot.
14
+ - The durable run record is limited to 16 MiB, including embedded settlement order. Payload admission reserves at least 64 KiB for control metadata, with additional space for outstanding step and settlement diagnostics. The reservation reduces usable payload capacity.
15
+ - Progress logs retain the newest 200 entries, each truncated to 2,000 characters. Checkpoint prompts are truncated to 10,000 characters. Reports allow at most 100 evidence strings; summary and evidence strings are each limited to 20,000 characters.
16
+
17
+ ## Measured capacity
18
+
19
+ `bun run verify:workflow-capacity --steps 1000 --timeout 900` passes all eight scenarios on OpenCode 2.0.14 and Bun 1.4.0:
20
+
21
+ | Measurement | Result |
22
+ | --- | --- |
23
+ | Sustained run | 1,000 steps, 1,000 unique native worker sessions, ordered results |
24
+ | Sustained elapsed time | 229.823 seconds |
25
+ | Owner pool | Two runs share eight concurrent workers; 16 unique sessions across both runs |
26
+ | Service RSS at eight blocked workers | 565,493,760 bytes, about 539 MiB |
27
+ | Pause | 0.242 seconds; eight active workers drain, eight queued workers do not start |
28
+ | Stop | 0.120 seconds; eight active native requests interrupted |
29
+ | One-second worker deadline | Failure observed after 1.321 seconds, including dispatch and teardown |
30
+ | One-second run deadline | Failure observed after 1.149 seconds |
31
+ | Host-call boundary | 108 accepted; 109 rejected when `maxAgents` is 1 |
32
+ | Log retention | Newest 200 of 205 entries retained |
33
+ | Cleanup | Zero active sessions; all 1,016 worker identities from the sustained and shared-pool scenarios retained |
34
+
35
+ The measured source hash is `1414fd9d27ead4a1f955f11168917842aaef043a8eec172b7bd73699aaf3e414`. These timings describe a deterministic provider on one machine, not real-model throughput. RSS is one service sample, not peak memory. The result does not establish capacity at 32 workers, across multiple owners, or over a seven-day run.
36
+
37
+ Separate native regressions exercise the nesting boundary. Focused tests exercise byte-size limits and simultaneous failure diagnostics. The 35-cycle runtime soak checks CPU termination and native thread cleanup; it does not load-test 64 simultaneous interpreters.
38
+
39
+ ## Native OpenCode limits
40
+
41
+ The inspected V2 schema defines agent `steps` as a positive integer and exposes no global native-session concurrency ceiling. This is not proof of unlimited capacity. `experimental.subagent_depth` defaults to 1. These native-generation controls are separate from workflow concurrency and total steps. The user's current configuration sets depth to 2 and the `general` agent to 20 steps.
42
+
43
+ ## Practical recommendation
44
+
45
+ Use at most eight workflow workers per owner, keep the default four for ordinary interactive use, and increase to eight only for an isolated load or known I/O-bound work. Prefer batches of 100–250 concise steps even though 1,000 are admitted. Store large evidence in artifact files and return paths. Keep reports far below 1 MiB so the 16 MiB aggregate journal retains headroom. Do not treat the configured maximum of 32 managed workers as a verified operating target.
46
+
47
+ The harness writes measured evidence to `.audit/workflow-capacity/evidence.json`. The capacity fixture explicitly configures eight workers. The plugin's four-worker default can be overridden with `maxWorkers`.
@@ -0,0 +1,105 @@
1
+ # Dynamic workflow verification
2
+
3
+ The hardening pass targets the seven defects reproduced against `5858de1`, plus storage and timeout races found during integration. The original blanket readiness claim is superseded by these checks and the limits below.
4
+
5
+ Current verification uses OpenCode `2.0.14`, Bun `1.4.0`, and published `@opencode/codemode` `2.0.12`. Earlier baseline and failing-before evidence used OpenCode `2.0.12`.
6
+
7
+ ## Version 0.2.0 release verification
8
+
9
+ The release adds configurable `workflowConcurrency` and `workflowMaxAgents` defaults for new runs. Explicit run values take precedence, and resumed records retain their original limits. The persisted schema defaults remain 3 concurrent agents and 4 total steps.
10
+
11
+ Release verification passed on OpenCode 2.0.14:
12
+
13
+ - `bun run typecheck` and all 109 unit tests / 407 assertions.
14
+ - 13 native regression cases against the extracted npm package, including configured 8/8 defaults, explicit 2/2 overrides, eight completed steps, and resumed 3/4 limits.
15
+ - 27 native workflow/TUI checks and 27 managed-session checks against the extracted package with fresh production dependencies.
16
+ - Independent source review: **PASS WITH NOTES**, with no release-blocking findings.
17
+
18
+ The packed source hash is `3b5b77f200d79164227c3caeb86eeab95f4c1188b8f7d5c1f689296e1022df72`. The package includes both entrypoints, all runtime worker modules, and the authoring skill with its runtime reference. Local release artifacts are in `.audit/release-0.2.0/`.
19
+
20
+ ## Hardening checkpoint checks
21
+
22
+ | Command | Result | Coverage |
23
+ | --- | --- | --- |
24
+ | `bun run typecheck` | Pass | Server, runtime, and terminal types |
25
+ | `bun test` | 109 tests, 407 assertions pass | Confinement, replay, checkpoint atomicity, journal limits, deadline races, failed-attempt accounting, and existing Threads behavior |
26
+ | `bun run verify:workflow-regressions` | 12 cases pass | Original engine defects, CPU-bound deadline, saved and nested execution, composition helpers, and retained-worktree handoff |
27
+ | `bun run verify:roles` | 17 checks pass | Configured profiles, inherited restrictions, role admission, and native delegation |
28
+ | `bun run verify:live` | 27 checks pass | Managed-worker lifecycle, reports, limits, native tabs, and restart recovery |
29
+ | `bun run verify:workflow-runtime` | Pass | 35 CPU-bound interpreter cancellations, stable native thread count, and idle CPU |
30
+ | `python3 scripts/verify-workflows-model.py` | Pass | Model-authored workflow, real VERA readers, validated handoffs, and automatic coordinator notification |
31
+ | `bun run verify:workflows` | 27 checks pass | Native workflow lifecycle, permissions, hard restart, uncertain writes, fresh navigator snapshots, keyboard step navigation, and terminal controls |
32
+ | `bun run verify:workflow-capacity --steps 1000 --timeout 900` | 8 scenarios pass | Eight concurrent workers, 1,000 unique native sessions, owner pool sharing, controls, deadlines, and cleanup |
33
+
34
+ Native harnesses use real isolated OpenCode services with deterministic local providers. These fixtures prove execution behavior rather than model quality. The separate real-model check uses the installed plugin and configured VERA profiles.
35
+
36
+ Each workflow, regression, and capacity harness records a hash of `index.ts`, `tui.ts`, `package.json`, and direct TypeScript sources in `src/`. It rejects source changes during its run. Local artifacts are retained under `.audit/` and are gitignored:
37
+
38
+ - `workflows/evidence.json` and `workflows/tui.screen.txt`
39
+ - `workflow-regressions/evidence.json` and `workflow-regressions/evidence.baseline.json`
40
+ - `workflow-capacity/evidence.json`
41
+ - `runtime-soak/evidence.json`
42
+ - `workflow-model/evidence.json`
43
+
44
+ The commands regenerate the evidence. Raw transcripts and temporary session directories are not published.
45
+
46
+ The final workflow, regression, and 1,000-step capacity runs all verified source hash `1414fd9d27ead4a1f955f11168917842aaef043a8eec172b7bd73699aaf3e414`.
47
+
48
+ ## Regression and recovery proof
49
+
50
+ The native regression harness first reproduced six engine failures on the old source. The runtime tests separately reproduced the synchronous deadline failure. All twelve native cases now pass:
51
+
52
+ 1. Queued agents cannot dispatch after the measured token budget is exhausted.
53
+ 2. Failed attempts count toward that budget.
54
+ 3. Answering one checkpoint preserves `waiting` when another remains unanswered.
55
+ 4. Concurrent checkpoint responses replay in their recorded order after restart.
56
+ 5. An exposed failure remains a failure on replay, preserving the script's fallback branch.
57
+ 6. A crash after an accepted report requires explicit same-worker resolution.
58
+ 7. Saved scripts accept new arguments and retain pinned nested source after restart.
59
+ 8. Composition helpers execute through the native service.
60
+ 9. Saved commands register and refresh.
61
+ 10. A fifth nested workflow boundary fails.
62
+ 11. A writer and verifier use the same retained worktree.
63
+ 12. A CPU-bound script reaches its deadline while service RPC remains responsive.
64
+
65
+ The hard-crash fixture appends one line, blocks before its report, and kills the service. After restart, no provider request is allowed until explicit recovery. Resume preserves the uncertain write. A follow-up asks the same worker to inspect and report its existing effect; the final file still has exactly one line.
66
+
67
+ Checkpoint responses and settlement order are committed atomically. Focused tests reject oversized responses before persistence, allow a smaller retry, and retain a concurrently committed agent settlement. Legacy external journals remain preserved during migration. Payload admission reserves diagnostic space while retaining the 16 MiB hard limit.
68
+
69
+ The macOS runtime soak starts and cancels 35 CPU-bound interpreters. Native thread count returns from 25 to 25, and the following idle second consumes 2.157 ms of process CPU. The check uses the real CodeMode interpreter inside terminable Bun workers.
70
+
71
+ ## Independent review
72
+
73
+ Independent review is a release gate. The initial audit rejected unqualified readiness and supplied executable counterexamples. Later review found a constructor-failure capacity leak, checkpoint journal poisoning, insufficient control headroom, and recovery failures that affected healthy sibling runs. Those findings received focused regression tests and fixes.
74
+
75
+ The final integrated engine/store review returned **PASS WITH NOTES**, with no blocking findings. Its remaining timer-cleanup finding was reproduced and fixed: a full legacy journal now clears the deadline and notifies the owner even when failure persistence also fails. The focused test preserves both original records.
76
+
77
+ A separate UI reviewer identified the stale navigator snapshot, then returned **PASS WITH NOTES** after the fix. Explicit refreshes are serialized, background requests coalesce, and responses are guarded across owner navigation. The native terminal suite passes all 27 checks. The A→B→A generation guard has source review but no dedicated navigation regression.
78
+
79
+ Accepted review limits include unsupported selective deletion of journal records, replay-order mismatches waiting until the run deadline, and reliance on a single loaded scheduler implementation. Checkpoint responses appear both in the checkpoint and its settlement entry, so they count twice toward the journal limit. A nearly full run can reject even a small response while preserving the unanswered checkpoint. Passing implementation-worker reports alone are not treated as independent approval.
80
+
81
+ ## Real-model verification
82
+
83
+ Run `wfr_8eafe136e930d91c0127b44532be49a4` completed through coordinator `ses_f35910071ffe0I7E0NR3Bzfvfk`, using `vera-core`:
84
+
85
+ - `vera-operator-readonly`, `openai/gpt-5.6-sol#low`, read `src/workflow-types.ts`.
86
+ - `vera-engineer-readonly`, `openai/gpt-5.6-sol#medium`, read `src/workflow-rpc.ts`.
87
+ - Validated result: `{"limits":{"concurrency":3,"maxAgents":4},"controls":["pause","resume","stop"]}`.
88
+
89
+ Both worker models matched their profiles and journal records. Their transcripts contain successful reads and accepted results. After the automatic notification, the coordinator inspected the run and delivered a PASS receipt.
90
+
91
+ ## Execution boundaries
92
+
93
+ - One OpenCode service with one loaded scheduler implementation owns scheduling. Multiple services or duplicate module instances sharing storage are unsupported.
94
+ - Interrupted writes require inspection and same-worker resolution. A missing worker does not authorize repeating its effects.
95
+ - Scripts, arguments, and nested scripts are immutable within a run. Changes require a new run key.
96
+ - Native failures and explicit `FAIL` or `INCONCLUSIVE` reports prevent completion, even when the script catches them.
97
+ - Token budgets govern dispatch using reported usage. In-flight work can exceed the threshold; unmeasured usage blocks further budgeted dispatch.
98
+ - Old journals without checkpoint settlement order cannot replay answered checkpoints deterministically. They fail with a diagnostic rather than inventing an order.
99
+ - Corrupt and exact-limit legacy records retain their evidence and produce owner-visible diagnostics. A full legacy record may require explicit repair or a new run key.
100
+ - Selectively deleting a run record while retaining its legacy completion journal is unsupported. Both records belong to the same run identity.
101
+ - An inconsistent settlement order can wait until the run deadline. Recovery does not invent missing completions to make the script advance.
102
+ - Interpreter termination stops script CPU work. Parent-side host effects remain subject to native interruption and uncertain-write recovery.
103
+ - Worktrees remain available for inspection and integration. The coordinator owns the final engineering verdict.
104
+
105
+ The [capacity findings](workflow-capacity-findings.md) distinguish cumulative sessions, concurrent agents, interpreter calls, and measured operating limits.
@@ -0,0 +1,60 @@
1
+ # Run a dynamic workflow
2
+
3
+ Describe the work with `/workflow-run`. OpenCode loads the authoring contract, writes a JavaScript script, and starts the run in the background.
4
+
5
+ ```text
6
+ /workflow-run Audit src/auth and src/billing for missing authorization checks. Use VERA reviewers, confirm each finding independently, and return source references.
7
+ ```
8
+
9
+ The run has a stable ID. Your conversation stays available while its agents work. Each worker uses a native OpenCode session with its selected profile, model, and permissions.
10
+
11
+ ## Inspect progress
12
+
13
+ 1. Run `/workflows`.
14
+ 2. Select a run to open its panel.
15
+ 3. Select a step with the arrow keys and press **Enter**, or click it, to open its native worker conversation.
16
+
17
+ The panel shows phases, step outcomes, verdicts, evidence, retained directories, recorded usage, and the final result. Step counts include work recorded so far; a dynamic script can add more steps. Press `f` for the full-screen view or `Esc` to close the panel.
18
+
19
+ The navigator lists every run owned by the current coordinator session, including completed and failed runs. The detail panel shows one selected run. Other coordinator sessions have their own run lists.
20
+
21
+ Ask the agent to inspect the run when you need its complete saved script or structured handoffs:
22
+
23
+ ```text
24
+ Inspect that workflow and summarize the confirmed findings and missing evidence.
25
+ ```
26
+
27
+ ## Pause, stop, and resume
28
+
29
+ Press `p` in the run panel to pause new scheduling. Active steps finish before the run becomes paused.
30
+
31
+ Press `x` to stop the run and interrupt its active workers. Completed results and worktree directories remain available.
32
+
33
+ Press `r` to resume. For a waiting checkpoint, enter a JSON response. For example, enter `true`, `42`, or a quoted string.
34
+
35
+ After a service restart, reopen the original conversation and select the run with `/workflows`. Resume reconciles its existing sessions before scheduling more work. It does not assume that an interrupted write left the directory unchanged.
36
+
37
+ For an uncertain write, ask the agent to inspect the retained worker and directory. Send the resolution request to that same worker. After the worker reports and finishes, resume the run. Use the [recovery contract](../skills/workflow-authoring/references/runtime.md#start-and-control) for crash and legacy-journal limits.
38
+
39
+ ## Save a useful workflow
40
+
41
+ 1. Select its run in `/workflows`.
42
+ 2. Press `s`.
43
+ 3. Enter a new name.
44
+ 4. Select **Project** or **User**.
45
+
46
+ The saved file contains the script. Run arguments and worker conversations stay in the original run. Existing files are not overwritten.
47
+
48
+ Invoke `/workflow-<name>` to reuse the script with new input. Project scripts live in `.opencode/workflows/`. Personal scripts live in the `workflows/` directory under your OpenCode configuration.
49
+
50
+ An edited script starts a new run. A run's script and arguments remain fixed so resuming it cannot silently reuse results from different instructions.
51
+
52
+ After editing a saved file directly, run `/workflow-refresh` to reload its command. The names `run` and `refresh` are reserved.
53
+
54
+ ## Use VERA roles
55
+
56
+ Ask for VERA when the task needs its engineering and evidence rules. The workflow chooses configured role IDs such as `vera-engineer` and `vera-auditor-readonly`; `/setup-vera` remains the place to change their models.
57
+
58
+ Keep an implementation and its runtime verification on the same retained worktree. Give the auditor the changed paths and verification evidence. The root conversation inspects the artifacts and owns integration and the final verdict.
59
+
60
+ For script syntax, limits, tools, and recovery semantics, read the [runtime contract](../skills/workflow-authoring/references/runtime.md). The [implementation plan](dynamic-workflows-plan.md) describes module ownership and verification requirements.
package/index.ts CHANGED
@@ -3,6 +3,7 @@ import type { SessionContext } from "@opencode/plugin/promise/session";
3
3
  import { z } from "zod";
4
4
  import { Report, ThreadsRpc, WorkerView } from "./src/rpc";
5
5
  import { Send, Spawn, WorkerTarget, threads } from "./src/threads";
6
+ import { workflows } from "./src/workflows";
6
7
 
7
8
  export default Plugin.define({
8
9
  id: "op-threads",
@@ -121,5 +122,6 @@ export default Plugin.define({
121
122
  },
122
123
  });
123
124
  });
125
+ return workflows(ctx, workers, models, limit);
124
126
  },
125
127
  });
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@op1/threads",
3
- "version": "0.1.7",
4
- "description": "Visible top-level worker sessions for OpenCode V2, with native tabs and durable reports.",
3
+ "version": "0.2.0",
4
+ "description": "Visible worker sessions and durable dynamic workflows for OpenCode V2.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
7
7
  "exports": {
8
8
  ".": "./index.ts",
9
9
  "./tui": "./tui.ts"
10
10
  },
11
- "files": ["index.ts", "tui.ts", "src/", "skills/"],
11
+ "files": ["index.ts", "tui.ts", "src/", "skills/", "docs/workflows.md", "docs/workflows-verification.md", "docs/workflow-capacity-findings.md", "docs/dynamic-workflows-plan.md"],
12
12
  "repository": {
13
13
  "type": "git",
14
14
  "url": "git+https://github.com/opzero1/threads.git"
@@ -27,19 +27,35 @@
27
27
  "verify:live": "uv run --with pyte python scripts/verify-live.py",
28
28
  "verify:roles": "python3 scripts/verify-roles.py",
29
29
  "verify:idle-tabs": "uv run --with pyte python scripts/verify-idle-tabs.py",
30
- "verify:tabs": "uv run --with pyte python scripts/verify-tab-groups.py"
30
+ "verify:tabs": "uv run --with pyte python scripts/verify-tab-groups.py",
31
+ "verify:activity": "uv run --with pyte python scripts/verify-activity.py",
32
+ "verify:workflows": "uv run --with pyte python scripts/verify-workflows.py",
33
+ "verify:workflow-regressions": "python3 scripts/verify-workflow-regressions.py",
34
+ "verify:workflow-capacity": "python3 scripts/verify-workflow-capacity.py",
35
+ "verify:workflow-runtime": "bun scripts/verify-workflow-runtime.ts"
31
36
  },
32
37
  "dependencies": {
38
+ "@opencode/codemode": "2.0.12",
33
39
  "@opencode/plugin": "2.0.7",
34
40
  "@opencode/schema": "2.0.7",
41
+ "acorn": "8.15.0",
42
+ "ajv": "8.17.1",
43
+ "effect": "4.0.0-rc.112",
44
+ "fuzzysort": "3.1.0",
35
45
  "zod": "4.1.8"
36
46
  },
37
47
  "peerDependencies": {
48
+ "@opentui/core": "0.5.10",
49
+ "@opentui/solid": "0.5.10",
38
50
  "solid-js": ">=1.9.0"
39
51
  },
40
52
  "devDependencies": {
53
+ "@opencode/theme": "2.0.12",
54
+ "@opentui/core": "0.5.10",
55
+ "@opentui/solid": "0.5.10",
41
56
  "@types/bun": "1.4.0",
42
- "solid-js": "1.9.9",
57
+ "opentui-spinner": "0.0.7",
58
+ "solid-js": "1.9.12",
43
59
  "typescript": "5.9.3"
44
60
  }
45
61
  }
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: Workflow authoring
3
+ description: Author and run durable dynamic JavaScript workflows in OpenCode with parallel agents, structured handoffs, and resumable progress.
4
+ ---
5
+
6
+ # Workflow authoring
7
+
8
+ Use a workflow for broad independent work or a pipeline whose later steps consume earlier results. Keep small, tightly coupled tasks with one owner.
9
+
10
+ 1. Define the result, named slices, and verification predicate. For VERA tasks, retain the selected protocol and role profiles.
11
+ 2. Read [the runtime contract](references/runtime.md). Write a script with a literal `export const meta` first, named agent steps, and a final JSON result.
12
+ 3. Call `workflows_start` with a unique task key, `script` or saved `name`, JSON `args`, and the smallest useful concurrency and agent limit. It returns immediately.
13
+ 4. Continue independent work. The runner delivers a final notification. Use `workflows_inspect` for evidence and `workflows_control` for pause, stop, resume, and checkpoint responses. Never infer success from an idle worker.
14
+ 5. Inspect the result and the affected artifacts before giving the final verdict. Save a useful script with `workflows_save`; its arguments and transcripts are not saved with it.
15
+
16
+ Every step selects an actual configured `agent`. Use `vera-operator-readonly` for discovery, `vera-engineer` for implementation, and `vera-auditor-readonly` for independent review. Model choices remain in the profiles. Workflow workers are leaves. The runner bounds the complete run, including nested workflows.
17
+
18
+ Use `access: "read"` for investigations. It denies edits and shell commands. Real command-based verification needs a suitably permitted worker with `access: "write"`, even when its intended task is only running tests. Use `isolation: "worktree"` for independent implementations and return the retained directory and changed paths. Give subsequent verification the same directory. The root owns integration.
19
+
20
+ Verification evidence is part of the handoff. `FAIL` and `INCONCLUSIVE` cannot establish a passing step. Do not replace execution evidence with reviewer votes. A workflow does not expand the user's authorization for external actions.
21
+
22
+ ## Example
23
+
24
+ ```javascript
25
+ export const meta = {
26
+ name: "module-audit",
27
+ description: "Audit named modules and return source-backed findings",
28
+ };
29
+
30
+ await phase("Audit");
31
+ const findings = await pipeline(args.modules, module => agent(
32
+ `Audit ${module} for ${args.check}. Read the actual source. Return concrete findings with paths and line numbers.`,
33
+ {
34
+ key: `audit:${module}`,
35
+ agent: "vera-engineer-readonly",
36
+ access: "read",
37
+ schema: {
38
+ type: "object",
39
+ properties: { findings: { type: "array", items: { type: "string" } } },
40
+ required: ["findings"],
41
+ additionalProperties: false,
42
+ },
43
+ },
44
+ ));
45
+ return findings;
46
+ ```
47
+
48
+ Invoke with structured arguments, for example `args: { modules: ["src/auth", "src/billing"], check: "missing authorization checks" }`. Set `maxAgents` to cover the named slices and any verification steps. Built-in defaults are four total agents and three concurrent agents; plugin options can override them. The `workflows_start` schema shows the configured defaults.
49
+
50
+ The example returns candidate findings. Add an independent confirmation stage when the task requires a verified report.