opencode-goal-plugin 0.6.4 → 0.6.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.6 — 2026-07-29
4
+
5
+ - Shard persisted goal state and lifecycle ledgers by hashed OpenCode session ID so independent sessions can run concurrently in one project. Keep same-session single-writer leases, lazy session loading, crash recovery, and safe migration of aggregate, legacy, and XDG state.
6
+
7
+ ## 0.6.5 — 2026-07-12
8
+
9
+ - Make `/goal sequence` the canonical ordered multi-goal command, retain the previous command and mode spelling as input-only compatibility aliases, correct the public auditor snapshot mode type to `"normal" | "ordered"`, and align README archive and compatibility claims with verified behavior.
10
+
3
11
  ## 0.6.4 — 2026-07-12
4
12
 
5
13
  - Re-check session status and recent messages after an auto-continue cooldown, pause immediately for human intervention, Plan-agent switches, permission rejection, aborts, and provider errors, and abort an accepted continuation when the user takes control.
package/README.md CHANGED
@@ -36,15 +36,16 @@ surface and versioning expectations.
36
36
 
37
37
  ### OpenCode version compatibility
38
38
 
39
- Manually tested via the OpenCode TUI (`tmux` + real provider credentials, no mocks), verified against the plugin's own persisted state rather than terminal display alone:
39
+ Tested against real OpenCode 1.17.15 processes with live provider credentials and no mocked plugin hooks. State, ledger entries, and workspace files were checked independently of terminal or model prose:
40
40
 
41
41
  | OpenCode Version | Provider Tested | `/goal status` | Auto-continue | Evidence-gated completion | Hook Output Display |
42
42
  |---|---|---|---|---|---|
43
+ | 1.17.15 | opencode (`deepseek-v4-flash-free`) | ✅ Canonical tool | ✅ Checkpoint + idle continuation | ✅ Structured `goal_complete` claim | ⚠️ Command text routed to model; mutation guard verified |
43
44
  | 1.17.15 | opencode-go (`qwen3.7-plus`) | ✅ | ✅ | ✅ Self-corrected after one rejection (bare `[goal:complete]` with no evidence), then completed cleanly | ⚠️ Not displayed |
44
45
  | 1.17.15 | opencode-go (`glm-5.2`) | ✅ | ✅ | ✅ Clean `[goal:evidence]` + `[goal:complete]` on the first attempt | ⚠️ Not displayed |
45
46
  | 1.17.15 | deepseek (`deepseek-chat`) | ✅ | ✅ | ✅ Clean `[goal:evidence]` + `[goal:complete]` on the first attempt; also verified end-to-end via the [demo](demo/) — autonomously fixed a real bug and reported evidence-backed completion | ⚠️ Not displayed |
46
47
 
47
- `/goal status` and auto-continue are graded on **state correctness** (verified directly against the plugin's persisted state file: correct limits parsed, correct turn/stop accounting, correct completion detection) not on what's rendered in the terminal, since that's tracked separately as Hook Output Display.
48
+ `/goal status` and auto-continue are graded on **state correctness** (verified directly against persisted state: correct limits, turn/stop accounting, completion state, and file effects), not on terminal rendering. The `deepseek-v4-flash-free` canary suite additionally covers pause/resume across processes, blocker/restart, hard-process recovery, real host compaction, and stale-history clear enforcement. See [`docs/providers.md`](docs/providers.md) for the complete lifecycle matrix and session evidence.
48
49
 
49
50
  **Note:** Hook output display depends on OpenCode version — on 1.17.15, `command.execute.before`'s `output.parts` text is not rendered in the TUI for any provider tested; the raw command argument is instead routed to the model as a normal chat turn (see [Limitations](#limitations)). State mutations always work regardless of display: goal creation, flag parsing, auto-continue, limit enforcement, and evidence-gated completion detection were all verified correct via the persisted state file in every combination above. Re-test against your own OpenCode build before relying on unattended runs, and see [`docs/providers.md`](docs/providers.md) for the full per-model marker-compliance notes.
50
51
 
@@ -89,7 +90,7 @@ Add success criteria, constraints / non-goals, and a mode:
89
90
  /goal ship the release --success "tests pass and changelog updated" --constraints "do not touch the public API" --mode ordered
90
91
  ```
91
92
 
92
- `--success` (alias `--success-criteria`) and `--constraints` (alias `--non-goals`) take quoted text and are injected alongside the objective so the assistant keeps them in view. `--mode` is `normal` (default) or `ordered` (alias `sisyphus`); `ordered` asks the assistant to work through the objective as a strict sequence. Multi-word values must be quoted.
93
+ `--success` (alias `--success-criteria`) and `--constraints` (alias `--non-goals`) take quoted text and are injected alongside the objective so the assistant keeps them in view. `--mode` is `normal` (default) or `ordered`; `ordered` tells the assistant to preserve step order inside one objective. To queue distinct objectives that auto-promote one at a time, use `/goal sequence`. Multi-word values must be quoted.
93
94
 
94
95
  Flags accept either `--flag value` or `--flag=value`. If a flag is unknown, missing a value, given a non-positive integer, or (for `--mode`) an unrecognized mode, the plugin rejects the command with a helpful error instead of silently folding the bad flag into the goal text.
95
96
 
@@ -125,7 +126,7 @@ Pause without clearing the active goal:
125
126
  /goal pause
126
127
  ```
127
128
 
128
- Clear the active goal:
129
+ Clear all live goals in the current session and discard their saved status:
129
130
 
130
131
  ```
131
132
  /goal clear
@@ -155,14 +156,14 @@ A session can hold more than one goal. `/goal <condition>` replaces the focused
155
156
  /goal focus 1
156
157
  ```
157
158
 
158
- `/goal list` shows the numbered live goals (which is focused, which are backgrounded) and a per-session archive of completed/cleared goals so they stay readable. `/goal focus <number>` switches the active goal, backgrounding the previous one. Focus is tracked per session and survives a restart.
159
+ `/goal list` shows numbered live goals (focused and backgrounded) plus achieved goals retained in the per-session archive. `/goal clear` intentionally removes live goals and saved status from these views; its terminal ledger entries remain available for crash-safe recovery decisions. `/goal focus <number>` switches the active goal, backgrounding the previous one. Focus is tracked per session and survives a restart.
159
160
 
160
- #### Ordered (sisyphus) sequences
161
+ #### Ordered sequences
161
162
 
162
- `/goal sisyphus` sets up a strict execution sequence: separate the objectives with `;` or newlines, and the plugin runs them one at a time, auto-focusing the next as soon as the current one completes.
163
+ `/goal sequence` sets up a strict execution queue: separate objectives with `;` or newlines, and the plugin runs them one at a time, focusing the next as soon as the current one completes.
163
164
 
164
165
  ```
165
- /goal sisyphus build the parser; write the tests; ship the release
166
+ /goal sequence build the parser; write the tests; ship the release
166
167
  ```
167
168
 
168
169
  The first goal is focused and the rest are queued. `/goal list` marks the session as ordered. Auto-promotion stops when the sequence is exhausted; `/goal clear` ends the sequence.
@@ -187,7 +188,7 @@ With success criteria, constraints, and a token budget shorthand:
187
188
  An ordered sequence, run as a strict pipeline:
188
189
 
189
190
  ```
190
- /goal sisyphus build the parser; write the tests; ship the release
191
+ /goal sequence build the parser; write the tests; ship the release
191
192
  ```
192
193
 
193
194
  ## How it works
@@ -238,7 +239,7 @@ Markers must appear on their own final line. The bracketed form is canonical, bu
238
239
 
239
240
  **Wrap-up vs. hard stop.** When a limit is reached, the plugin sends one final prompt asking the assistant to summarize what is done, what remains, and the next concrete step — rather than stopping silently. Use `/goal resume` to continue after any stop, including limit stops and no-progress pauses.
240
241
 
241
- Goal state is persisted by default to a **project-local** path, `.opencode/goals/state.json` relative to the working directory, so goals follow the project rather than your home directory. It is only a local workflow checkpoint and is not synchronized across machines or OpenCode instances. You may want to add `.opencode/goals/` to your `.gitignore`.
242
+ Goal state is persisted by default to a **project-local** namespace rooted at `.opencode/goals/state.json` relative to the working directory, so goals follow the project rather than your home directory. Each OpenCode session gets a separate hashed shard at `<stateFilePath>.sessions/<sha256(sessionID)>/state.json`, allowing unrelated sessions in the same project to run concurrently. The state is local and is not synchronized across machines. You may want to add `.opencode/goals/` to your `.gitignore`.
242
243
 
243
244
  The state-file location is resolved with this precedence:
244
245
 
@@ -246,11 +247,11 @@ The state-file location is resolved with this precedence:
246
247
  2. the `OPENCODE_GOAL_STATE_PATH` environment variable, if set;
247
248
  3. the project-local default `<cwd>/.opencode/goals/state.json`.
248
249
 
249
- When the default path has no state yet, the plugin migrates forward from older locations on first load: the legacy `~/.opencode-goal-plugin/state.json` and the XDG path `${XDG_STATE_HOME:-~/.local/state}/opencode-goal-plugin/state.json`. Migration is exclusively claimed; after the project-local write succeeds, the legacy file is retired to a timestamped `.migrated…` backup so another project cannot import the same private goal state. An explicit `stateFilePath` or `OPENCODE_GOAL_STATE_PATH` is used literally with no migration fallback.
250
+ When a project has no shard namespace yet, the plugin migrates all sessions from older locations on first session access: the legacy `~/.opencode-goal-plugin/state.json` and the XDG path `${XDG_STATE_HOME:-~/.local/state}/opencode-goal-plugin/state.json`. Migration is exclusively claimed; after every session shard is written, the source files are retired to timestamped `.migrated…` backups so another project cannot import the same private goal state. An explicit `stateFilePath` or `OPENCODE_GOAL_STATE_PATH` is used as the shard namespace root and has no migration fallback.
250
251
 
251
252
  The state directory is created with owner-only permissions, and the JSON state file is written as `0600` because it may contain goal text, assistant checkpoints, and workflow history.
252
253
 
253
- Alongside the state file the plugin keeps an **append-only lifecycle ledger** (`<stateFile>.ledger.jsonl`, also `0600`). Every lifecycle event — set, edit, auto-continue, pause, resume, blocked, completed, limit — is appended as one JSON line. Because the in-memory history is capped, the ledger is the durable record: if the main state file is missing or corrupted, the plugin reconstructs still-active (non-completed) goals from the ledger on startup and reloads them in the paused recovery state. Terminal events (complete/blocked) are written to the ledger *before* the main state write, so a goal's terminal outcome survives even if that write fails (**fail-closed**); such a failure is logged at error level.
254
+ Alongside each session shard the plugin keeps an **append-only lifecycle ledger** (`<shard>/state.json.ledger.jsonl`, also `0600`). Every lifecycle event — set, edit, auto-continue, pause, resume, blocked, completed, limit — is appended as one JSON line. Because the in-memory history is capped, the ledger is the durable record: if a session state file is missing or corrupted, the plugin reconstructs still-active (non-completed) goals from that session's ledger on startup and reloads them in the paused recovery state. Terminal events (complete/blocked) are written to the ledger *before* the state write, so a goal's terminal outcome survives even if that write fails (**fail-closed**); such a failure is logged at error level.
254
255
 
255
256
  Recovered active goals are loaded in a **paused** state with a recovery note, so unattended auto-continue does not resume blindly after a restart. Set `"persistState": false` to keep purely in-memory behavior (this also disables the ledger).
256
257
 
@@ -272,7 +273,7 @@ Override any limit for a single goal:
272
273
  | `--no-progress-turns <n>` | Consecutive stalled low-output turns before pausing |
273
274
  | `--success <text>` | Success criteria that define when the goal is satisfied (quote multi-word text) |
274
275
  | `--constraints <text>` | Constraints / non-goals to respect (alias `--non-goals`) |
275
- | `--mode <normal\|ordered>` | Execution mode; `ordered` (alias `sisyphus`) asks for a strict sequence |
276
+ | `--mode <normal\|ordered>` | Prompt mode for one goal; `ordered` preserves step order inside its objective |
276
277
  | `--no-tool-turns <n>` | Consecutive tool-free continuation turns before pausing |
277
278
 
278
279
  Examples:
@@ -327,7 +328,7 @@ Additional plugin-level options:
327
328
  - `goalAgentName` / `verifierAgentName` — customize the registered native agent names (defaults `goal` and `goal-verify`). The verifier is a hidden subagent with a default-deny tool policy; only `read`, `glob`, and `grep` are allowed.
328
329
  - `sdkShape` — OpenCode session-client argument shape: `legacy` (the default generated `PluginInput` client using `{ path, body, query }`) or `flat` (clients using `{ sessionID, ... }`). Read-only `messages`/`get` calls may probe the alternate shape after an argument/schema `TypeError`; mutating calls are never replayed, so set this option correctly for embedded clients.
329
330
  - `persistState` — whether to persist active goals and recent goal results to disk.
330
- - `stateFilePath` — where the persisted state JSON is written. Overrides the default project-local path and the `OPENCODE_GOAL_STATE_PATH` env var. Useful if you want a fixed or ephemeral location. When unset, the default is `<cwd>/.opencode/goals/state.json` (see the persistence section above), and `OPENCODE_GOAL_STATE_PATH` can override it without editing config.
331
+ - `stateFilePath` — root path for the persisted session-shard namespace. Overrides the default project-local path and the `OPENCODE_GOAL_STATE_PATH` env var. Useful if you want a fixed or ephemeral location. When unset, the default root is `<cwd>/.opencode/goals/state.json`; shards are written below `<stateFilePath>.sessions/` (see the persistence section above).
331
332
  - `ledgerMaxBytes` / `ledgerRetentionFiles` — bound the lifecycle ledger to 2 MiB per generation and three rotated generations by default. Set retention to `0` to discard the active ledger when it reaches the size ceiling.
332
333
  - `resultRetentionMs` — how long a completed goal summary remains available through `/goal status` after the goal leaves active memory.
333
334
  - `maxStoredResults` — maximum number of completed-goal summaries retained in process memory before the oldest ones are evicted.
@@ -343,7 +344,7 @@ Registered tools:
343
344
 
344
345
  `goal_set` and `set_goal` are explicitly constrained to user-requested goals. `goal_complete` accepts a structured claim: a required non-empty `summary`, plus optional criterion/evidence pairs, checks (`passed`, `failed`, or `not-run`), changed files, and known limitations. Failed checks and empty criterion evidence are rejected before archival; accepted claims are serialized deterministically for the configured completion auditor. The legacy `update_goal` tool retains its string `evidence` field for compatibility.
345
346
 
346
- These operate on the same per-session multi-goal state as the command path: a tool-set goal persists, shows up in `/goal list`, and is driven by the idle auto-continue; completing a goal in an ordered (sisyphus) sequence auto-promotes the next.
347
+ These operate on the same per-session multi-goal state as the command path: a tool-set goal persists, shows up in `/goal list`, and is driven by the idle auto-continue; completing a goal in an ordered sequence auto-promotes the next.
347
348
 
348
349
  > Integration note: the tool execute-context shape (`ctx.sessionID`) and the `tool.schema` surface follow the OpenCode plugin docs. The tool **logic** is unit-tested independently, but the live registration should be confirmed against a real OpenCode run (see the smoke-test checklist).
349
350
 
@@ -388,7 +389,7 @@ OpenCode's current `command.execute.before` hook does not fully intercept comman
388
389
 
389
390
  The plugin depends on `experimental.chat.system.transform` and other OpenCode plugin hooks that may change between OpenCode versions.
390
391
 
391
- Only one persistence-enabled OpenCode plugin instance may own a given `stateFilePath` at a time. A second process fails initialization with the owning PID/host instead of risking last-writer-wins state loss. Dispose the first host or configure a different path.
392
+ Distinct OpenCode sessions may own shards under the same `stateFilePath` concurrently. A second process using the same session shard is still rejected with the owning PID/host instead of risking last-writer-wins state loss; use the original session or wait for its owner to dispose.
392
393
 
393
394
  ## Diagnostics and recovery
394
395
 
@@ -10,6 +10,8 @@ The latest published release is the supported line. Public compatibility covers:
10
10
  - the documented OpenCode hook names
11
11
  - the six canonical goal tools and five legacy tool aliases
12
12
  - persisted-state recovery from versions documented in the changelog
13
+ - concurrent persistence for distinct OpenCode sessions in one project, with
14
+ single-writer protection retained per session
13
15
 
14
16
  The package requires Node.js 18 or newer and OpenCode 1.17.15 through the latest
15
17
  compatible 1.x release. CI runs the complete unit suite on Node 18, 20, 22, and
package/index.d.ts CHANGED
@@ -48,7 +48,7 @@ export interface GoalAuditSnapshot {
48
48
  condition: string
49
49
  successCriteria: string
50
50
  constraints: string
51
- mode: "normal" | "sisyphus"
51
+ mode: "normal" | "ordered"
52
52
  sessionID: string
53
53
  turnCount: number
54
54
  startedAt: number
@@ -222,17 +222,19 @@ export interface GoalPluginOptions {
222
222
  persistState?: boolean
223
223
 
224
224
  /**
225
- * Filesystem path where persisted goal state is written when
226
- * `persistState` is enabled. Overrides both the project-local default
227
- * and the `OPENCODE_GOAL_STATE_PATH` environment variable.
225
+ * Root filesystem path for persisted session-shard state when
226
+ * `persistState` is enabled. Each session is written below
227
+ * `<stateFilePath>.sessions/<sha256(sessionID)>/state.json`. Overrides both
228
+ * the project-local default and the `OPENCODE_GOAL_STATE_PATH` environment
229
+ * variable.
228
230
  * @default "<cwd>/.opencode/goals/state.json"
229
231
  */
230
232
  stateFilePath?: string
231
233
 
232
234
  /**
233
- * Filesystem path for the append-only lifecycle ledger
234
- * (`<event> per line`, used to reconstruct active goals if the main
235
- * state file is missing or corrupted).
235
+ * Legacy aggregate filesystem path for the append-only lifecycle ledger.
236
+ * During migration, events are partitioned into per-session shard ledgers;
237
+ * new writes use `<shard>/state.json.ledger.jsonl`.
236
238
  * @default "<stateFilePath>.ledger.jsonl"
237
239
  */
238
240
  ledgerFilePath?: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-goal-plugin",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Durable, guarded goal workflows for OpenCode.",
5
5
  "type": "module",
6
6
  "main": "./src/goal-plugin.js",