@cleocode/goal 0.0.0-stage → 0.9.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/CHANGELOG.md +319 -0
- package/LICENSE +21 -0
- package/README.md +141 -2
- package/cli/ocg.ts +149 -0
- package/dist/cli.js +8708 -0
- package/dist/tui.d.ts +2 -0
- package/dist/tui.js +1920 -0
- package/package.json +79 -4
- package/rpc.ts +2 -0
- package/server.ts +3 -0
- package/skills/write-goal/SKILL.md +211 -0
- package/skills/write-goal/assets/goal.template.yaml +38 -0
- package/skills/write-goal/references/examples.md +283 -0
- package/skills/write-goal/references/goal-schema.md +53 -0
- package/skills/write-goal/references/interview.md +36 -0
- package/src/cli/install.ts +125 -0
- package/src/cli/status.ts +116 -0
- package/src/contract/parse.ts +454 -0
- package/src/contract/render.ts +134 -0
- package/src/contract/types.ts +158 -0
- package/src/engine/amendment.ts +51 -0
- package/src/engine/amendments.ts +72 -0
- package/src/engine/cleo-bridge.ts +339 -0
- package/src/engine/cleo-link.ts +150 -0
- package/src/engine/continue-work.ts +131 -0
- package/src/engine/decisions.ts +44 -0
- package/src/engine/dogfood-record.ts +278 -0
- package/src/engine/handoff.ts +102 -0
- package/src/engine/interrupt-policy.ts +45 -0
- package/src/engine/inventory.ts +70 -0
- package/src/engine/launches.ts +82 -0
- package/src/engine/loop-stop.ts +50 -0
- package/src/engine/loop.ts +109 -0
- package/src/engine/next-work.ts +82 -0
- package/src/engine/owner-intent.ts +71 -0
- package/src/engine/owner-reviews.ts +178 -0
- package/src/engine/peer-announce.ts +67 -0
- package/src/engine/peer-transport.ts +78 -0
- package/src/engine/policy.ts +42 -0
- package/src/engine/registry.ts +119 -0
- package/src/engine/similarity.ts +65 -0
- package/src/engine/stale-decisions.ts +68 -0
- package/src/engine/state.ts +185 -0
- package/src/engine/store.ts +263 -0
- package/src/engine/tick-record.ts +129 -0
- package/src/engine/tick.ts +207 -0
- package/src/rpc.ts +182 -0
- package/src/server/app.ts +4320 -0
- package/src/server/help.ts +50 -0
- package/src/server/index.ts +23 -0
- package/src/server/intent.ts +22 -0
- package/src/server/messages.ts +228 -0
- package/src/summary/audit.ts +76 -0
- package/src/summary/build.ts +314 -0
- package/src/tui/dashboard.ts +322 -0
- package/src/tui/decision-form.ts +12 -0
- package/src/tui/format.ts +127 -0
- package/src/tui/index.tsx +718 -0
- package/src/tui/manager.ts +94 -0
- package/src/tui/navigation.ts +11 -0
- package/src/tui/pointer-select.ts +20 -0
- package/src/tui/tracer.ts +51 -0
- package/src/util/git.ts +102 -0
- package/src/util/ids.ts +20 -0
- package/src/util/shell.ts +42 -0
- package/src/verify/artifact.ts +95 -0
- package/src/verify/pipeline.ts +200 -0
- package/tui.tsx +2 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.9.0 — 2026-10-17
|
|
4
|
+
|
|
5
|
+
- **Published to npm as `@cleocode/goal`.** The package was `opencode-goal` (private, installed from git tags). It is now public with npm provenance. The plugin id stays `opencode-goal`, so existing configs, state and the TUI keep working. A tag-triggered `release.yml` publishes through npm Trusted Publishing (OIDC, no token).
|
|
6
|
+
- **The bin runs on plain Node.** `cleo-goal` and `ocg` point at `dist/cli.js`, a Node bundle built from `cli/ocg.ts` by `scripts/build-cli.mjs` and committed like `dist/tui.js` (CI fails when it is stale). The tick, status and install paths no longer use Bun APIs: git and owner check commands run through `node:child_process`, the Stop payload is read from `process.stdin`, and goal contracts parse with the `yaml` package when Bun's built-in parser is absent (OpenCode still uses Bun's).
|
|
7
|
+
- **`cleo-goal --version`, `status --json` and `install --project`.** The contract CLEO reads (`schemaVersion: 1`). `install --project [--spec] [--json] [--dry-run]` writes only the project's `.opencode/opencode.json` (`plugins`), is idempotent, replaces an older spec of this plugin in place (keeping its options), and refuses the home directory, user-global config, invalid JSON and a `.jsonc` config without writing. A `.opencode` dir or `opencode.json` symlinked into `~/.config`, `$XDG_CONFIG_HOME`, `~/.opencode` or anywhere outside the project is refused; a symlinked `opencode.json` inside the project is written through.
|
|
8
|
+
- **CI.** Typecheck, unit tests, the scripted-harness test under Bun and Node, and a packed-bin smoke on a PATH without Bun, on every push and pull request.
|
|
9
|
+
|
|
10
|
+
## 0.8.2 — 2026-10-17
|
|
11
|
+
|
|
12
|
+
- **Packaging-only preparation for `@cleocode/goal`.** The package name, public access and README install path were prepared. **Superseded by 0.9.0 (PR #2) and never published to npm.**
|
|
13
|
+
|
|
14
|
+
## 0.8.1 — 2026-10-17
|
|
15
|
+
|
|
16
|
+
- **The post-goal summary is scannable (T120).** The summary dialog now opens with a 3–5 second digest — `completed`, `WENT RIGHT`, `WENT WRONG`, `STILL OPEN`, `NEXT`, linked tasks, and a one-line provenance split (`host 6 · verifier 2 · owner 1`) — capped at 14 lines; the full report (criteria, non-goals, findings) stays in `text`. Palette actions use Title Case. The RPC summary event carries `title` and `digest`.
|
|
17
|
+
- **A pending decision is asked once, then silent (T121/T123).** `shouldReaskDecision()` is now a pure policy: stale wins, an open form never duplicates, a dismissed (`interrupted`) review never re-asks, and an already-asked review never re-asks. The once-per-process marker lives at module scope, so a plugin re-setup cannot silently ask again; a real process reload clears it and re-presents once. `restorePendingDecision()` recovers the latest pending decision after a reload.
|
|
18
|
+
- **Fixed — an explicit review request restores a dismissal.** The once-per-process marker also blocked the documented `goal_review` restore path, because the direct ask is skipped while the session is mid-turn and the deferred re-ask was suppressed. `reviewDecision` now clears the marker for that exact decision; the protected `durable-goal-decisions` oracle caught it before release.
|
|
19
|
+
- **The loop prefers leaves over epics (T122/T123).** `readyCleoTask()` asks `cleo next -n 5`, skips sagas/epics, and returns the first leaf; skipped parents are journalled as `continue-parent-skipped`. If only parents are ready, the loop treats it as no work instead of opening a goal for a rollup. The design note records the work-vs-state rule.
|
|
20
|
+
- **Recorded.** CLEO lifecycle records for T119/T122 are committed with the fixes; T001 stays closed with `docs/cleo-t001-completion.md` as its one-off receipt.
|
|
21
|
+
- **Release posture.** A git-tag patch only. **Nothing was published to npm.**
|
|
22
|
+
|
|
23
|
+
## 0.8.0 — 2026-10-17
|
|
24
|
+
|
|
25
|
+
- **A clean restart resumes by itself (C1).** A host restart no longer asks for a click when the lock is unchanged and the run is intact: the run re-arms its wait, re-verifies a claim it was verifying, or receives the continuation it had pending. A contract changed under the run, a repaired/divergent run and an interrupted write still pause and name the cause. The decision is the pure `restartDecision` (`src/engine/interrupt-policy.ts`).
|
|
26
|
+
- **An accepted amendment resumes in the same act (C2).** Acceptance previously left the run paused at "owner accepted amendment; resume when ready", so every amendment cost a second owner decision. It now clears the pause, records `resumed` + `amendment-resumed`, and admits the ordinary continuation with no new question — through both the native review and genuine Composer chat. A recovered acceptance (host died after the commit) resumes on reload.
|
|
27
|
+
- **A stale decision is never re-presented (C3).** Answered, expired, superseded and generation-changed reviews are silenced with a `decision-silenced` ledger event; only a dismissed (`interrupted`) review is still unanswered and may be restored. `goal_review` refuses a stale review instead of minting a replacement.
|
|
28
|
+
- **Completion starts the next ready CLEO item by itself (C4).** `cleo next` → `cleo show --full` → a contract generated from the task's own acceptance (verifier criteria), linked back to the task id, then a fresh session and a real run. The start is a notification plus `continue-work` ledger events; no completion decision is created. A project with no ready item keeps today's behaviour.
|
|
29
|
+
- **Idle availability announcements over AgentMBX (C5).** One outstanding announcement at a time, a 15-minute cooldown floor, bounded reply polling, and replies recorded as candidate work (never consent). The channel is injectable/off by default (`OCGOAL_PEER_IDENTITY`); tests use a fake `agentmbx` and touch no network.
|
|
30
|
+
- **The stop rules are a tested policy (C6).** Human criteria, blockers and outward/destructive acts always stop. A first visit to a new project and an exhausted budget also stop, with explicit contract opt-outs (`autonomy.visit_new_projects`, `autonomy.continue_past_budget`). The contract's stated intent reads the opposite for those two; that contradiction is recorded in the design note rather than silently resolved.
|
|
31
|
+
- **A fresh launch carries a handoff (D1).** `handoff.md`/`handoff.json` beside the contract, built from the host's own facts — prior run, lock, red baseline, HEAD/branch/dirty state, first plan step, authority vs stale docs — and a bounded digest in the turn-1 kickoff. It is labelled context, not authority. This was the first `fresh: true` launch in 22 launches; the prior 21 started inside the session that already knew everything.
|
|
32
|
+
- **An unknown check key fails validation (D6).** `expect.stderr_contains` used to be silently dropped and defaulted to `exit: 0`, weakening a criterion to "the command exited". Unknown expectation keys are now errors naming the key and the supported set.
|
|
33
|
+
- **Documentation.** `docs/research/proactive-continuation.md` states the sources and order, the announcement rate limit, the stop rules, what is never automatic, and the release posture; `docs/research/session-handoff.md` diagnoses the fresh-launch gap; `docs/HANDOFF-v0.8.0.md` is the session handoff.
|
|
34
|
+
- **Release posture.** A new minor, published as an immutable git tag and installed from it. **Nothing was published to npm.**
|
|
35
|
+
|
|
36
|
+
## 0.7.1 — 2026-10-16
|
|
37
|
+
|
|
38
|
+
- **The tick no longer crashes on an unreadable store (T102).** v0.7.0 named the payload and transcript degradations but left the *store* half unguarded: `ocg tick` read the goals directory and `run.json` with no `try/catch`, so an I/O error (permissions, `ENOTDIR`) escaped as an exception. Empty stdout means "no live goal" to the harness dispatcher, so a crash was indistinguishable from "nothing to do" — which silently **drops** the run and ends the session. An unreadable store, run record or contract is now a stated degradation: the tick logs what it could not read and **holds** the session (`{"final":false}`) rather than printing nothing on a guess, and a record it cannot read is never replaced by an invented one. A readable live run beside an unreadable record is still ticked, and the unreadable record is named.
|
|
39
|
+
- **The bin survives an unexpected throw (T102).** `ocg tick` catches an error from the tick, holds the session and puts the reason on stderr, instead of dying with an empty stdout.
|
|
40
|
+
- **C1's suite now covers the store clause (T102).** `test/unit/tick-record.test.ts` gains the absent-store, empty-store, unreadable-store, unreadable-record, unreadable-record-beside-a-live-run, unreadable-contract, unparsable-contract and "wrote nothing on degradation" cases.
|
|
41
|
+
- **Why this is a patch and not a re-cut (T102).** v0.7.0 was tagged and pushed, and then this defect was found by *this goal's own C1* before v0.7.0 was ever loaded into a session. The fix is published **forward** rather than rewriting the published v0.7.0 tag, because the tag is public history and immutability is exactly what C5 demands. C5's check is a `--min-version 0.7.0` floor and C6 is written for the `v0.7.x` line, so the v0.7 release this goal loads is v0.7.1. Nothing was published to npm.
|
|
42
|
+
|
|
43
|
+
## 0.7.0 — 2026-10-16
|
|
44
|
+
|
|
45
|
+
- **The tick RECORDS the turn (T102)** — `ocg tick` no longer decides from a frozen run state. Before deciding it advances the turn, recomputes the repository fingerprint (a move counts as progress), honours a claim the run already carries, and — best-effort and optional — mines a transcript the harness Stop payload points at for tool calls and a progress call. Everything is written through OCG's own store, so the records, the ledger and the schema stay identical to the adapter's, and the stall pause, recovery, the budget wrap-up and the blocker repeats now work under a harness that is not OpenCode.
|
|
46
|
+
- **It runs the verification of a claim (T102)** — a claim sets the run to `verifying`, the tick runs the host checks, records the results and the verdict, and either completes the run (printing nothing, so the harness session can end) or hands the agent the verdict as its next continuation. A goal whose verification is host-only can therefore be driven to `complete` from another harness; a goal asking for the independent verifier child still needs the OpenCode adapter, and the note says so.
|
|
47
|
+
- **`ocg claim` (T102)** — the agent-side claim surface for a harness with no OCG tools: it records a claim on the live run through OCG's own store, so the next tick can verify it. (OpenCode agents keep using the `goal_claim` tool.)
|
|
48
|
+
- **Honest degradation, named rather than guessed (T102)** — a missing or non-JSON Stop payload, a payload naming no transcript, an unreadable transcript and a transcript with no content are each logged as `could not see …` to stderr, and the turn is still recorded with the fingerprint the tick can take. It never invents a tool call, a progress call or a live-check transition.
|
|
49
|
+
- **Proven by a scripted harness (T102)** — `test/host/tick-scripted-harness.test.ts` drives a real goal from kickoff to `complete` using only the bin: the harness ticks, a scripted agent does the work and claims through `ocg claim`, and the next tick verifies and completes. No OpenCode anywhere.
|
|
50
|
+
- **Naming (owner decision)** — the package becomes `@cleocode/goal` ("CLEO Goal") with the bin `ocg`, documented as "open cleo goal", and `cleo-goal` as an equally supported long form, so CLEO's default `ocg tick` keeps working. The rename and the Node-runnable bin are the packaging goal that follows; nothing was published here (an `npm pack --dry-run` only).
|
|
51
|
+
|
|
52
|
+
## 0.6.0 — 2026-10-16
|
|
53
|
+
|
|
54
|
+
- **The loop's decision is a pure function (T102)** — `decide(state, signals, limits) -> action` now lives in `src/engine/loop.ts` with no filesystem, network or OpenCode types, and the OpenCode adapter *applies* its action instead of holding the logic. Every case the adapter took is pinned by `test/unit/loop-decision.test.ts` (a claim beats a continuation; the wrap-up turn precedes the budget stop; a stall precedes the ordinary continuation; the owner-set `on_stall` policy in both directions; the wait deadline arithmetic; recovery selection), and the existing goal-loop suites prove the adapter's behaviour is unchanged.
|
|
55
|
+
- **An `ocg` bin with a `tick` subcommand (T102)** — it implements CLEO's merged Stop-dispatcher contract (`#2306`) exactly: the harness Stop JSON on **stdin**, **JSON only on stdout** (`{"decision":"block","reason":…}` to keep the agent working, `{"final":false}` to hold the session), every log line on **stderr**, and **nothing at all** when there is no live goal or no `.opencode/goals`. The reason is the **same continuation** `renderTailNote` produces for the decided `TailKind`, so a harness driven through tick behaves identically to OpenCode for the decision it makes. It is read-only, and it deliberately does **not** install its own Stop hook — CLEO's dispatcher owns Stop.
|
|
56
|
+
- **The parity is documented (T102)** — `docs/research/harness-agnostic-loop-decision.md` carries the decision contract, the case-by-case parity table against the adapter, and an honest statement of what the tick does not do yet (it does not advance turn counters or the stall streak).
|
|
57
|
+
- **npm publication metadata prepared (T102)** — a `bin` entry for `ocg` and the `cli` folder in `files`. **Nothing was published**: no `npm publish`, no registry credential, and the package is still installed from a git tag.
|
|
58
|
+
- **Fixed** — the tick first treated `archived` as a terminal status, which is not one of the run states; it now uses the real terminal set (`complete`, `failed`, `aborted`, `superseded`), so a failed or superseded goal is not mistaken for a live one.
|
|
59
|
+
|
|
60
|
+
## 0.5.2 — 2026-10-16
|
|
61
|
+
|
|
62
|
+
- **The dogfood cache is bound to the SERVING ENGINE (T102)** — the reuse key now hashes the serving pid, location and endpoint alongside the release SHA and the artifact hashes. A record produced by a different (restarted or replaced) engine is never reused, which is what silently broke the manager flow: the v0.5.1 chain reused an interview record from pid 24041 after the server had moved to pid 30270, so the manager driver refused with `QA serving PID changed` before it could exercise anything. A cache that cannot be reused is now labelled `stale-cache` **by name**, with `cacheMismatch` reporting whether it was the release, the artifacts or the engine that changed — and that label now takes precedence over the generic incomplete-flow label, because the mismatch is the root cause.
|
|
63
|
+
- **An export↔task coherence check (T102)** — `bun scripts/dogfood-t102-coherence.ts` fails when the export's manager verdict and CLEO task T102 disagree in either direction: a task closed while the manager flow did not pass would be a closure on a technicality, and a manager pass with the task still open would be an unrecorded result.
|
|
64
|
+
- **A multi-task goal's contract states the commit-citation rule (T102)** — when a goal links more than one CLEO task, the rendered contract requires every run commit to cite **all** of them (e.g. `feat(T123,T124): …`), because the work lands on `main` with no PR or task branch and `cleo done --commit <head>` binds a task only if its message cites it (CLEO PR #2305). Without it the uncited tasks stay open with a "neither cites" blocker.
|
|
65
|
+
- **Per-flow export detail now names the endpoint too** — each flow's section gives the serving pid, location and endpoint it actually ran against, next to the release SHA and artifact hashes.
|
|
66
|
+
|
|
67
|
+
## 0.5.1 — 2026-10-16
|
|
68
|
+
|
|
69
|
+
- **A consolidated exact-release real-model dogfood workflow (T102)** — `scripts/real-model-dogfood.ts --assert` runs the five existing native flows (interview/handoff, manager, lifecycle, completion, amendment) against the **installed** release in one bounded pass, records for each the exact release SHA, the serving PID and location, the model/agent selections and the server/UI/skill artifact hashes, preserves the bounded raw owner messages, forms and tool calls, and labels every flow **honestly**: `pass`, `fixture`, `stale-cache`, `partial` or `timeout`. Only a verified `pass` is reusable, and only while its release and artifact hashes still match — so a start rehearsal and the completing check do not pay for five real-model flows twice, and a recorded failure is never cached as an answer.
|
|
70
|
+
- **A readable, two-place evidence export (T102)** — the committed summary `docs/research/real-model-dogfood/<version>.md` (plus a `.json` index) and the raw per-flow records under `.opencode/dogfood/<release>/`. It keeps raw hashes, request ids and bounded read coordinates only — never credentials or full provider payloads — and states plainly what it does not claim.
|
|
71
|
+
- **The three `satisfies:` AC-reference forms (T102)** — a criterion's `proves:` accepts `T123#AC2`, `T123#<uuid>` and `T123#AC2@<14-digit pin>`; a locked contract should prefer the uuid or pinned form, because a bare alias drifts when a task's criteria are renumbered. `cleo.tasks[].acs` is still read for compatibility but is now warned as deprecated in favour of the per-criterion binding.
|
|
72
|
+
- **`reportSha256` (T102)** — every scoped test report is recorded with the sha256 of its bytes beside its path, so a future `cleo goal close` can bind `test-run:` to exactly those bytes.
|
|
73
|
+
- **Fixed** — `acsByGateFromProves` no longer maps a `diff`, `verifier` or `human` criterion onto `implemented`: a diff check is a guard (it passes when paths are *unchanged*), and a verdict alone never proves `implemented` or `testsPassed`. Those ACs stay open and are reported.
|
|
74
|
+
|
|
75
|
+
## 0.5.0 — 2026-10-16
|
|
76
|
+
|
|
77
|
+
- **CLEO is an optional first-class citizen (T117)** — opencode-goal probes for the `cleo` CLI and the running project's `.cleo` workspace at setup and on demand, and reports exactly three states: **CLI absent**, **CLI present but the project uninitialized**, or **initialized**. `/goal cleo` shows the state and the exact command; `install` and `init` run that command **only on your explicit choice** (`npm i -g @cleocode/cleo`, `cleo init`) — never silently, never with sudo, and never inferred. A project with no CLEO records nothing and behaves byte-for-byte as before.
|
|
78
|
+
- **A goal can name the CLEO tasks it serves (T117)** — the optional `cleo.tasks` block links task ids (with an optional per-gate `acs` claim), and a criterion may carry the precise per-criterion binding `proves: ["T123#AC2"]`. Both are parsed and validated; `/goal cleo` is documented in the help builder and pinned by a unit test so the help cannot drift.
|
|
79
|
+
- **The goal→CLEO evidence bridge is CLI-only and never fabricates a closure (T117)** — at completion, and on demand via `/goal cleo sync [<slug>] T123[@sha] …` or `bun scripts/cleo-sync.ts`, it reads CLEO's own AC map, derives each task's change-set from the commits that reference it, runs a scoped report over every test file the change adds or edits, records genuine per-gate evidence (`implemented`, `testsPassed`, `qaPassed`) and completes the task through the `cleo` CLI only. An AC no evidence proves stays **open** and is reported — it is never bound by position, and `cleo complete` is never called with an open AC. The engine never opens `.cleo` or its database.
|
|
80
|
+
- **T080, T081 and T082 are closed through that bridge (T080, T081, T082)** — on real evidence (their own commit, a scoped report over the changed test files, and the typed gate run), the case that defeated the manual pass.
|
|
81
|
+
- **Coordination with CLEO (T117)** — `docs/research/cleo-goal-integration-proposal.md` records the proposed goal→CLEO evidence and AC-signoff protocol, the multi-task-goal case, the exact mbx messages to `cleo-dev-mac`, their replies, and the review of CLEO PR #2297 (an inverted `diff` docstring, and the `criteria[].proves` acceptance gap now implemented).
|
|
82
|
+
- **Fixed** — a stale `test/unit/decision-form.test.ts` assertion that predated T090's deliberate removal of the TUI's decision-form reply (the engine owns the reply; the double-execution property stays covered by `ask-tool-decisions`); and README staleness (the `v0.1.0-alpha.1` pin, the missing `goal_*` tools, and `/goal cleo`).
|
|
83
|
+
|
|
84
|
+
## 0.4.9 — 2026-10-16
|
|
85
|
+
|
|
86
|
+
- **A run can bind its exact execution workspace (T099, T100)** — a contract may name `workspace: .worktrees/<slug>`. When set, host checks, the protect/diff base and the progress fingerprint resolve against that linked worktree instead of the shared project root, so a protected oracle changed *inside* the worktree is finally guarded and the run's base commit is the workspace's HEAD. Omitting it keeps today's root behaviour; a workspace that is not a git work-tree root inside the project refuses the start, and `.opencode/goals/**` still resolves at the project root. The reviewed decision (`docs/research/exact-run-workspace-migration-decision.md`) records the scoped evidence (the VidaPeps case), the candidate options, the chosen disposition, its reversibility and the residual risk. No other project's goal state was read-modified, and no existing run was migrated.
|
|
87
|
+
- **Guarded disposition of manually divergent goal contracts, planned read-only (T101)** — `docs/research/manual-divergence-disposition-plan.md` names the detection (locked snapshot vs disk bytes), the three owner-review options with their consequences, how the divergent bytes are preserved, and what is explicitly refused (hand-editing a lock, blessing a noncanonical `resolved`, editing another project).
|
|
88
|
+
- **Lab lifecycle and reporting findings consolidated (T103–T106)** — `docs/research/lab-lifecycle-triage-findings.md` records what each task is covered by, naming the specific existing oracle and what remains open, without claiming an unreproduced fix.
|
|
89
|
+
|
|
90
|
+
## 0.4.8 — 2026-10-16
|
|
91
|
+
|
|
92
|
+
- **The decision and amendment lifecycle now has explicit AC oracles (T093–T098, T080, T082, T085)** — seven focused tests assert the exact contracts: per-criterion approval binds to the exact decision/run/lock/criterion (a stale or unknown decision is refused and flips nothing); a focused manager action dispatches exactly once on the selected target, and a redundant or mismatched one returns explicit scoped feedback instead of a silent no-op (no other session is touched); an amendment is accepted only from genuine, unambiguous owner chat (quoted, conditional, pre-staging, expired and worker-only text refused); a pre-form acceptance reaches the resolver without a fabricated reply, and later contradictory input wins; an owner continuation is carried from the owner's own words (a true hint alone is not consent, plain acceptance stays stopped); a failed duplicate launch keeps the live run and never recommends aborting it; and a cancelled decision is re-presented without inferring a choice and executes exactly once.
|
|
93
|
+
- **No behavioural change.** These ACs were already implemented; the existing protected suites also prove them (criterion-approval, manager-actions, composer-amendment, duplicate-launch, durable-goal-decisions, decision-dialogs, ask-tool-decisions, amendment-review-recovery, amendment-resume-prerequisite, banner-transition). This patch makes that coverage explicit and immutable. Native pointer/Enter focus is TUI-only and is asserted at the host/RPC contract boundary plus the existing banner oracle; no native screenshot is claimed.
|
|
94
|
+
|
|
95
|
+
## 0.4.7 — 2026-10-16
|
|
96
|
+
|
|
97
|
+
- **An owner resume during a busy turn takes effect immediately and exactly once (T091)** — pausing (or resuming) left the run's previous `wait` in place, so an immediate busy resume was resurrected into `waiting` at the turn's end and never received its continuation: the run looked resumed but was stranded. Pause and resume now clear the obsolete wakeup and wait, a resume is idempotent (an already-running run journals no second receipt and is not admitted twice), and exactly one continuation is admitted when the busy turn ends. The deferral carve-outs are unchanged: a resume behind a pending native form, a cross-session target, a terminal run or worker-sent consent still defers or refuses.
|
|
98
|
+
- **A waiting goal's claim or expired wait drains exactly once behind a busy turn (T092)** — a wait that expires while an owner turn is running still wakes exactly once afterwards, and a claim that ends a wait cancels the obsolete wakeup, so the old deadline does not verify or admit again.
|
|
99
|
+
|
|
100
|
+
## 0.4.6 — 2026-10-15
|
|
101
|
+
|
|
102
|
+
- **The contract names the verifier's model and effort (T107)** — `verification.model` (`providerID/modelID[#variant]`) and `verification.effort` now reach the verifier child; a value that cannot be parsed falls back to the previous resolution and journals `verifier-model-fallback` with `requested`, `reason` and `used`, so a typo never blocks a run.
|
|
103
|
+
- **Evidence quotes tolerate JUnit/XML escaping (T108)** — a verifier that quoted the decoded line it read from a junit file was rejected because the raw bytes carry ` `/`"`/`<`. `checkQuotes` now matches the raw bytes or their entity-decoded equivalent, for the raw junit and a plain-text renderer alike.
|
|
104
|
+
- **A commit-history criterion is answerable without a shell (T109)** — the verifier has only read/glob/grep, so "check the git history" was unanswerable and burned the round budget (the real C4/C5 cause). The host puts a bounded git digest in the child prompt and saves a quotable copy inside the project; no shell and no external path.
|
|
105
|
+
- **The per-round budget is testable below the 60s floor (T110)** — the floor is now the option `verifierMinRoundMs` (default 60_000), so the verdict-grace tests run in seconds instead of a minute each. A late `goal_verdict` is still used.
|
|
106
|
+
- **The stall notice names the policy (T111)** — `goal.paused.stalled` now names the owner-set `autonomy.on_stall` value and the owner's choices (`/goal resume`, `/goal abort`, or `on_stall: continue`).
|
|
107
|
+
- **Read-only calls write nothing under a goal folder (T115)** — `goal_status`, `goal_provenance` and `goal_inventory` no longer write anything under `.opencode/goals` (the provenance evidence used to be written by the read). The host records `engine-provenance.json` during verification instead, where the protected install checker expects it. Reported by VidaPeps.
|
|
108
|
+
- **The fingerprint sees linked-worktree work (T116)** — `fingerprint()` now includes `git worktree list --porcelain`, so a commit in `.worktrees/<slug>` records `changed:true` and no longer trips the stall auto-pause. Reported by VidaPeps.
|
|
109
|
+
- **Bounds of the live-goal write guard are documented (T112)** — `docs/research/live-goal-write-guard-bounds.md` names the mechanism, a captured refused-write/allowed-read example, and the residual risk a static path scan cannot see, without claiming full coverage.
|
|
110
|
+
- **Verifier diagnostics read the context correctly** — `verifierCause` and `verdictsFromTranscript` read `message.parts`/`state.output`, but this OpenCode carries parts under `message.content` and the denial as an object in `state.error`, so a real denial was misreported as an unknown timeout (pre-existing on v0.4.5; found while proving I1).
|
|
111
|
+
|
|
112
|
+
## 0.4.5 — 2026-10-15
|
|
113
|
+
|
|
114
|
+
- **A late goal_verdict is still an answer (T108)** — a verifier that calls `goal_verdict` just after the per-round budget was recorded as "did not call goal_verdict" and the round thrown away. There is now a short grace window after the budget, and if the call is in the captured transcript it is used (`verifier-fallback`, ledger `from: "transcript"`). A verifier that never decides is still a failure.
|
|
115
|
+
- **A slow round is not a denied one (T108)** — `verifierCause` scanned the whole transcript for "external_directory", so reading a file whose TEXT mentions it (the protected diagnostics test does) was reported as a permission denial. Only a tool result that actually says the permission was rejected counts, and a denial inside a granted `verification.read` root is not a denial either. This misfire narrated the goal engine's own verdict on this very work.
|
|
116
|
+
|
|
117
|
+
## 0.4.4 — 2026-10-15
|
|
118
|
+
|
|
119
|
+
- **Reading goal state is not writing it (T108)** — the new write guard was too broad: a shell command that merely read the run state through an interpreter (`open('<path>')` with no write mode) was refused. Only a write call or an explicit write mode counts now. Found by using the guard on this very goal; red before the fix (1 pass / 1 fail), green after (2 / 0), with the case pinned in the guard test alongside `cat`, `grep` and `ls`.
|
|
120
|
+
|
|
121
|
+
## 0.4.3 — 2026-10-15
|
|
122
|
+
|
|
123
|
+
- **Every write path into a live goal folder is refused (T108)** — the guard covered edit/write/patch only, so an agent hand-merged `run.json` with a shell command (the reported file had duplicate `updatedAt`, `tokens`, `tools` and `lastUsageEmit` keys). Shell commands and scripts are now checked too — redirection, `tee`, `sed -i`, `mv`/`cp`/`rm`/`truncate`, and interpreters that write the path — and every refusal names the supported tool: `goal_progress`, `goal_wait`, `goal_block`, `goal_flag`, `goal_claim`, `goal_manage`, `goal_recover_divergence`. Reads still work, and another project's or an archived folder is unaffected.
|
|
124
|
+
- **A corrupt run.json is proven, not guessed (T108)** — duplicate JSON keys are found by scanning the raw bytes, because `JSON.parse` silently keeps the last value. The run is then rebuilt from its own ledger `start` event and its locked contract snapshot (raw bytes kept beside the run as `run.json.corrupt-<ts>.json`, with `run-state-corruption` and `run-state-repaired` events); when those cannot prove the identity, the goal is refused and left to the owner instead of guessing.
|
|
125
|
+
- **Stalling stops punishing real work (T108)** — host-visible activity is recorded per turn, a `goal_progress` note counts as progress, and the stall reason says whether the run was active or idle. An owner-set `autonomy.on_stall: continue` keeps a stalled-but-active run going with a recorded `stall-report` instead of pausing; the default stays `pause`, and an idle run still stops. `run.json` keeps being written by the engine alone.
|
|
126
|
+
|
|
127
|
+
## 0.4.2 — 2026-10-15
|
|
128
|
+
|
|
129
|
+
- **A completed sibling no longer shadows the live goal (T106)** — a session can own several goals over time (finish one, start the next). On reload the engine mirrors every goal on disk into runs keyed by session, so the alphabetically-last folder won and a COMPLETE goal shadowed the running one: `goal_status`, the injected contract and the collected provenance all reported the finished goal. Found on the real v0.4.1 hot reload against this project. The mirror now keeps a live run and only skips a terminal sibling, recording `recovery-shadow-avoided` in the ledger.
|
|
130
|
+
|
|
131
|
+
## 0.4.1 — 2026-10-15
|
|
132
|
+
|
|
133
|
+
- **A divergence refusal names the fix (T106)** — when a goal's disk `goal.yaml` differs from its locked generation, the owner-chat amendment resolver, the amendment review, the resume/verify prerequisite and the invalid-goal refusal all name the cause with both hashes and the exact `goal_recover_divergence {"slug":…,"expectedLock":…}` call. A refusal whose cause is not divergence keeps its existing wording, and the divergence check reads the locked snapshot versus the disk bytes, never a pending proposal. axiom-lab-release answered an impossible "Accept amendment" 10+ times without ever being told why.
|
|
134
|
+
- **The impossible review is not re-offered (T106)** — while the disk differs, `goal_review` refuses to re-present the acceptance review (the staged review stays pending) and `goal_manage` offers no resume question. Once the divergence is resolved, reviews and resumes work normally again.
|
|
135
|
+
- **Adoption returns the refreshed target (T106)** — `goal_status` and the `goal_recover_divergence` result carry the `target` tuple (`key`, `lock`, `runId`, `sessionID`) that `goal_manage` accepts, and an identity-changed refusal names the refreshed tuple instead of only saying "refresh its details". axiom lost a round to `goal_inventory` after an adoption because the goal KEY changes to `live:<slug>:<runId>`.
|
|
136
|
+
|
|
137
|
+
## 0.4.0 — 2026-10-15
|
|
138
|
+
|
|
139
|
+
- **Recover a contract edited outside the engine (T101)** — when a live goal's `goal.yaml` differs from its lock, `goal_recover_divergence` (callable from any live session of the project) offers the owner the exact difference and last-writer provenance: Adopt disk version (a new journalled generation; changed criteria reset), Restore locked version (locked bytes written back, divergent bytes kept under evidence/) or Leave unchanged. An unaccepted proposal with matching bytes and a dead author is shown as provenance, never counted as consent, and is superseded by the owner's pick. This unblocks the shape where staging and acceptance both refused.
|
|
140
|
+
- **Owner-set agent autonomy (T101)** — `autonomy.amend: [timeout, budget, plan, recover]` lets the agent apply those changes itself through the same write-ahead journal as an owner acceptance, recorded `by: agent-policy`, announced in the banner and undoable with `/goal revert <slug>`. Changed criteria statements or checks, protect, scope, verification mode, stop rules or the policy itself are refused and routed to the owner. Default is today's owner-only behaviour.
|
|
141
|
+
- **Diagnosable verifier timeouts (T101)** — the child transcript is read before the round is interrupted (it used to record `messages: []`), keeps each tool call's name, input and bounded output, and the verdict names the cause, e.g. `denied external_directory access to /path/sibling`.
|
|
142
|
+
- **Opt-in verifier read paths (T101)** — `verification.read` lists folders outside the project the verifier may read. A per-scope verifier agent allows only those roots; writes and every other outside path stay denied.
|
|
143
|
+
- **Parallel launch slugs re-tested on OpenCode 2.0.29** — the protected oracle passes with per-boundary records; the historical 2.0.27 report remains unreproduced and is not claimed fixed.
|
|
144
|
+
|
|
145
|
+
## 0.3.15 — 2026-10-14
|
|
146
|
+
|
|
147
|
+
- **Actual owner chat can start a reviewed draft (T105, P0)** — an explicit start/continue-goal instruction is fetched from the authoring session, bound to the latest actually displayed unchanged contract and mode, and consumed once in a distinct admission receipt. Expired form answers stay expired; quoted, conditional, foreign, changed, ambiguous, child-session and spent authority cannot launch. A sole matching pending launch question can be unheld without accepting it. Existing live goals still use guarded management, not inferred chat replacement.
|
|
148
|
+
- **Resolve the prerequisite, not another impossible resume (T105, P0)** — an unresolved amendment is reported before a resume/verify confirmation is offered. An owned unpresented prerequisite can restore its exact diff without accepting or resuming. A live session can stage a fresh validated review for a paused target; only actual new owner acceptance and an applied journalled generation retire conflicting unanswered proposals. Old consent and authorship are never transferred.
|
|
149
|
+
- **Release only an obsolete owned question (T105)** — server plugins may lack Core's `form.cancel` capability. Successful amendment disposition uses neutral interruption only for a sole nonce-bound obsolete form; unrelated holds stay protected. Restart recovery retires journal-proven obsolete proposals, never guesses acceptance from mailbox liveness.
|
|
150
|
+
- **Banner truth follows a real run** — no expired receipt is relabeled as running. The supported owner instruction creates a real admission/run, then the existing reactive banner follows that identity. Hot plugin reload is sufficient to load this patch; restarting alone never authorizes a launch.
|
|
151
|
+
|
|
152
|
+
## 0.3.14 — 2026-10-13
|
|
153
|
+
|
|
154
|
+
- **Missing identity is not changed identity (T104)** — incomplete live-goal targets name the missing `runId` or `sessionID` and request the complete fresh-inventory tuple. Full identity comparisons and actual-owner confirmation remain mandatory; no implicit target normalization or fallback goal is introduced.
|
|
155
|
+
- **Already resolved is not new authority (T104)** — an owned settled amendment reports its recorded status/result with `alreadyResolved:true`, `actionExecuted:false`, and `ok:false`. It never consumes another owner message or repeats acceptance/continuation. Executing, expired, unknown, and foreign reviews remain separately refused.
|
|
156
|
+
- **Report the receipt before changed status (T104)** — a settled lifecycle decision names its actual disposition rather than blaming the target's expected post-operation transition. Pending target/generation guards remain intact; failed or uncertain outcomes never become success.
|
|
157
|
+
- **Keep parallel-launch evidence honest (T103)** — passive isolated boundary traces correlate each slug and call result, including three already-live parallel requests and sequential controls. The historical OpenCode 2.0.27/provider report is not reproduced on immutable v0.3.12 with OpenCode 2.0.28 and is not claimed fixed. Launch expiry and cross-session recovery policy are unchanged.
|
|
158
|
+
|
|
159
|
+
## 0.3.13 — 2026-10-13
|
|
160
|
+
|
|
161
|
+
- **A failed attempt is not a failed run (T098)** — Composer and manager launch text correlates actual session/slug/lock/run presence and leads with the existing non-terminal goal's status. A late failed duplicate review stays a failed audit receipt; it does not replace the live run, invent successful admission or tell the owner to abort a working goal. Changed-lock and unrelated failures retain their own recovery details.
|
|
162
|
+
- **Safe duplicate-start guidance (T098)** — an occupied session refuses new admission while preserving its run. Same-goal requests explicitly say continue/manage the existing run, not “Abort it first”; different-goal requests point to a fresh-session launch instead of discarding current work. Snapshots expose optional actual session/lock identity for correlation, without treating launch progress or reported plan steps as criterion proof.
|
|
163
|
+
|
|
164
|
+
## 0.3.12 — 2026-10-13
|
|
165
|
+
|
|
166
|
+
- **Owner continuation is not a model hint (T097)** — after accepting the exact amendment, the host honors an authenticated “resume”, “continue” or “get it done” instruction even if the agent omits the optional hint or supplies false. A true hint without that actual owner directive still refuses; plain acceptance remains stopped. The operation stays once-only and bound to the accepted next generation, never an approval of criteria.
|
|
167
|
+
- **Keep partial dogfood outcomes honest** — v0.3.11 delivered the queued real chat and atomically accepted the reviewed diff, but its real model supplied `continueGoal:false` and left the run paused. That evidence remains a failed end-to-end continuation check, not a retroactively relabeled pass. The observer also distinguishes a held waiting run from stronger host-proven completion; a claim or running/paused state cannot substitute.
|
|
168
|
+
|
|
169
|
+
## 0.3.11 — 2026-10-13
|
|
170
|
+
|
|
171
|
+
- **Chat before form creation (T096)** — retain a non-consenting, exact-review admission pointer when explicit acceptance, rejection or revision arrives before its native question binds. A later matching sole form is unheld once; unrelated forms and later contradictions remain protected. Application still requires the typed resolver to fetch and consume the actual persisted owner message; the pointer cannot approve or re-lock anything.
|
|
172
|
+
- **Use the verified V2 interruption field (T096)** — native `SessionInterruptInput` uses `resume`, not `continue`. Unholding with `resume:true` lets the already-admitted owner chat dispatch after question cancellation instead of stranding it in the inbox. The deployed v0.3.10 real-model timeout and delayed-form fixture failure exposed this timing path; the immutable older tag remains unchanged.
|
|
173
|
+
|
|
174
|
+
## 0.3.10 — 2026-10-13
|
|
175
|
+
|
|
176
|
+
- **Composer chat resolves amendments (T095)** — `goal_resolve_amendment` fetches the actual latest owner message, binds it once to the exact staged review/run/current and next locks, and uses the existing atomic commit. Explicit acceptance preserves unchanged proof; rejection/revision preserves the current contract. Acceptance alone stays stopped; an explicitly requested continuation is bound to the accepted next generation. Quoted, conditional, stale, synthetic, unrelated and replayed consent cannot authorize it.
|
|
177
|
+
- **A question must not swallow chat (T095)** — an explicit owner reply can unhold only the exact review's sole bound native form. Interruption itself neither accepts nor consumes consent; persisted-message validation still runs through the typed operation. Unrelated forms remain untouched. Bundled guidance keeps acceptance in the Composer, never a nonexistent sidebar button or manual amendment command.
|
|
178
|
+
- **Criterion-specific reviews (T093)** — rejected-criterion options carry an exact criterion ID; multiple rejections offer separate approvals. Recovery replaces an old missing-argument review with a fresh bound nonce instead of replaying failed consent. Launch activity/outcome notices use the shared actionable builders.
|
|
179
|
+
- **Return from an already-current goal (T094)** — explicit Open goal session closes its manager when that session is already current, rather than silently focusing the same covered tab. Enter and actual one-line pointer coordinates are regression-tested; unavailable navigation/action targets get inline feedback.
|
|
180
|
+
|
|
181
|
+
## 0.3.9 — 2026-10-12
|
|
182
|
+
|
|
183
|
+
- **Claims must actually verify (T092)** — a completion claim accepted while a goal is waiting now enters host verification at execution end instead of remaining at “claimed.” Verification cancels obsolete wait timers; it never manually marks criteria proven.
|
|
184
|
+
- **Wait deadlines survive holds (T092)** — an expired continuation keeps one quiet timer while execution or an unrelated form holds the session, then wakes once. It observes an updated wait deadline, preserves stopped/terminal guards, and lets a pending claim win over another worker admission. Focused real-host regressions reproduce all three failures on immutable v0.3.8 and check automatic completion, busy/form holds, and no extra wake.
|
|
185
|
+
- **Deployed completion dogfood** — isolated real-model QA can pin an immutable candidate without changing the original engine; its observer preserves that pin. A separate real-model completion driver records actual claim → host checks → proven terminal state, not merely acceptance of a claim.
|
|
186
|
+
|
|
187
|
+
## 0.3.8 — 2026-10-12
|
|
188
|
+
|
|
189
|
+
- **Owner resume is a control, not a queued job (T091)** — a direct `/goal resume` for this session's stopped run executes during an active inspection turn when no native form holds it. The existing exact state/contract/review checks remain; no competing kickoff is admitted. Unrelated forms and cross-session holds still defer. A real busy-turn regression first reproduced the paused host/snapshot, then verified one immediate resume.
|
|
190
|
+
- **Persistent fresh-run identity (T076)** — the original authoring sidebar now explicitly renders the goal slug and compact session identifier while its fresh run remains non-terminal. Full identifiers are available in expanded details. Reserve card height for that row and preserve expand-before-jump; the pointer disappears only when the run ends.
|
|
191
|
+
|
|
192
|
+
## 0.3.7 — 2026-10-12
|
|
193
|
+
|
|
194
|
+
- **Composer-only owner decisions (T086)** — remove duplicate lifecycle popups and their deferred-choice timers. Native Composer ask forms are the only decision surface. Lifecycle/amendment reviews persist until answered, cancelled or their exact target changes; sealed launch approvals and legacy timed records still expire. Manager decisions reveal the Composer rather than leaving Enter on the underlying action list; identical repeated reviews retain one nonce.
|
|
195
|
+
- **Visible admission and review recovery (T087)** — expired, changed, refused and failed launch outcomes now leave durable ledger/transcript receipts. Approved rehearsal shows starting/check progress without inventing a running goal. Native review recovery finds launches and amendments, requires a new real choice for expired timed consent, and never replays approval. Post-rehearsal contract/reload checks prevent a stale run.
|
|
196
|
+
- **Bounded execution errors (T088)** — successful prompt admission no longer erases execution failures. Only a real successful execution resets the streak; unavailable models pause immediately, transient errors stop at the configured limit, and retry timers are cleared on failure pause. No provider settings are changed.
|
|
197
|
+
- **Truthful progress feedback (T089)** — stopped `goal_progress` reports `recorded:false`, current status and recovery guidance without mutating plan/proof or resuming. Active reports remain journalled. Footer/cards distinguish agent-reported plan work from host-proven criteria.
|
|
198
|
+
- **Readable, operable terminal manager (T090)** — fixed native-theme header with Close/F9 return, compact status/session/plan/proof facts, word-wrapped scrollable details, pinned actions and Tab section focus. Track the actual mounted fullscreen slot when `panel.current()` is null; preserve selected-section input across resize and show operation results inline instead of focus-stealing popups. Long-content native tests and the real terminal gate cover resize, pointer/keyboard actions, close/reopen and fresh-tab focus.
|
|
199
|
+
|
|
200
|
+
## 0.3.6 — 2026-10-11
|
|
201
|
+
|
|
202
|
+
- **Truthful native popup delivery (T085)** — selecting a lifecycle popup before its exact native form exists now visibly says the operation has not executed; the real V2 `form.created` event wakes the bounded delivery queue. Expired reviews cannot submit a still-rendered form. Missing forms, expiry and failed host operations surface recovery guidance rather than silent acceptance. Successful host resolution carries the actual operation result. Target/nonce, source-consent and replay guards are unchanged.
|
|
203
|
+
- **Native popup proof** — compiled-UI tests cover delayed creation, unrelated forms, expiry and failure feedback; a host scenario proves late resume consent never executes. The real PTY probe observes the host popup's actual pointer-enabled row handlers/bounds rather than mistaking its custom list for a SelectRenderable or using transcript matches as controls. Fresh focus waits for both route navigation and the hydrated active tab.
|
|
204
|
+
- **Keep project scope deliberate** — current-session goals still rank first, then declared priority/recency; other project sessions remain marked elsewhere. Inventory display is not permission to act on another goal or a cross-project scheduler.
|
|
205
|
+
|
|
206
|
+
## 0.3.5 — 2026-10-11
|
|
207
|
+
|
|
208
|
+
- **Reactive Git-installed TUI (T084)** — ship deterministic Solid-universal `dist/tui.js`, leaving renderer/Solid imports external. Raw TSX under `node_modules` was excluded by the host's Solid transform: installed cards froze at their initial empty props even though source-only tests passed. Root entries remain intact. Native installed-package regressions now resolve the shipped JS without source TSX, then deliver inventory after mount and verify actual cards, expansion, jump, and selected actions.
|
|
209
|
+
- **Scope inventory before cache hydration** — fall back to the TUI location when session metadata is absent; missing/malformed inventory is an explicit recoverable error, not empty success.
|
|
210
|
+
- **Compiled-byte proof** — release provenance and immutable old-cache checks require/hash `dist/tui.js`; covering typecheck also rejects stale generated bytes.
|
|
211
|
+
|
|
212
|
+
## 0.3.4 — 2026-10-11
|
|
213
|
+
|
|
214
|
+
- **Don't ask twice for resume (T083)** — `goal_manage` reads the genuine owner message from host session history. **Resume**, **continue**, or explicit resume approval can authorize one operation on that same session/run/lock. **I accept** applies only to a current exact resume review. Steering doesn't revoke the directive; later pauses, changed generations, contradictory/conditional wording, synthetic inputs, and consumed messages cannot mint new authority. Consumption is atomic across store instances and survives restart. Launch/amendment review and evidence guards remain intact.
|
|
215
|
+
- **Keep runtime receipts out of source evidence** — owner-review JSON files and their transient lock/temp files are gitignored alongside run state, ledgers, and evidence. Contract YAML and actual source remain visible to Git/CLEO evidence checks. No tracked history is removed.
|
|
216
|
+
|
|
217
|
+
## 0.3.3 — 2026-10-11
|
|
218
|
+
|
|
219
|
+
The project goal-manager release. Natural language stays with the agent; typed host operations enforce exact targets, contract locks, and owner consent. The original conversation is never cleared.
|
|
220
|
+
|
|
221
|
+
- **Conversational entry (T081)** — `/goal` opens a create-or-manage flow; ideas and ordinary lifecycle language reach the write-goal interview rather than being mistaken for rigid command names. Native launch review offers revision, same-session launch, or a fresh root session. Approvals are durable, one-use, and bound to the reviewed session, slug, contract, and launch mode.
|
|
222
|
+
- **Staged amendments (T081)** — validated before/after diffs use native accept/reject/comment review. Owner acceptance commits a journalled generation change, retaining unchanged proof and invalidating changed statements, checks, essentialness, or invariant roles. Workers can't accept their own proposals.
|
|
223
|
+
- **Project inventory and stacked cards (T077)** — drafts, live/stopped/completed runs, and archives carry real session metadata and explicit unknowns. Bounded, width-safe sidebar cards show non-terminal goals across sessions, current first, with an accessible manager pointer. Reconnects and missed events converge. CLEO tasks aren't invented goal parents.
|
|
224
|
+
- **Interactive Goals tab (T078/T079)** — available from a goal-less primary session. Keyboard or pointer selection expands details before an explicit session jump. Pause, resume, verify, attach, and confirmed abort target the selected generation, never another session's goal.
|
|
225
|
+
- **Visible fresh handoff (T076/T079)** — opens and focuses the new session tab, with router fallback when tabs are disabled. The source model/agent is preserved or an explicit fallback is recorded; a durable pointer remains with the intact original history.
|
|
226
|
+
- **Decision recovery (T082)** — exact nonce/target correlation, replay protection, recoverable dismissal/commentary, and truthful failed-operation records. Dialog choices answer the matching native question instead of leaving a duplicate ask running and blocking subsequent commands.
|
|
227
|
+
- **Mounted feedback (T080)** — banners track replacement runs reactively; terminal and empty snapshots continue refreshing. Panel toggling uses host ownership rather than a stale local flag.
|
|
228
|
+
- **Artifact proof** — provenance records the serving PID/location, immutable Git revision, and server/UI/skill hashes. Release checks reject old cache bytes even when the version label matches. A seven-check real PTY gate covers two goals, pointer expansion/actions, keyboard navigation, narrow focus/bounds, native confirmation, and fresh tab focus. Deployment and real-model findings are recorded separately; passing source tests alone isn't installed-release proof.
|
|
229
|
+
|
|
230
|
+
## 0.3.2 — 2026-10-06
|
|
231
|
+
|
|
232
|
+
The trust-the-card release: the sidebar card always converges to disk truth, every card row is readable end-to-end, decisions ALSO ask through OpenCode's native in-composer ask tool, and the panel walkthrough is gated on a real pty. Every item is an owner-reported live defect from the v0.3.1 completion — fixed and proven under the goal loop itself (dogfood-5).
|
|
233
|
+
|
|
234
|
+
- **Fixed: the sidebar card froze at a stale snapshot across restarts (T072)** — two root causes. The complete transition reached `run.json` only if the summary builder's try-block survived its last line, so a reporting failure stranded the run as a zombie "running" on disk and the next server reload recovered it into "paused" while the run had actually finished (the lived paused/20-of-22/turn-7 card). The terminal write now persists at the transition itself, BEFORE the reporting layer, and pushes the final `updated` event; and `recover()` mirrors EVERY goal on disk — terminal ones included — so a re-attaching TUI's snapshot reads the disk truth (complete/N), never nothing. Proven by `test/host/refresh-convergence.test.ts`: reload mid-run → resume → complete → snapshot shows every criterion proven → a second reload keeps it complete.
|
|
235
|
+
- **Fixed: card rows truncate with no way to expand (T073)** — the compact card keeps its 12-line cap but always ends with an explicit expand affordance naming where the full rows live (`↳ F9 → Progress`); truncated rows carry a tab click-through target; the panel (the expanded view) renders every row FULLY at any width — statements wrap instead of ellipsizing, and the legend wraps to two short lines. `cardExpandedLines` renders the whole card uncut (all criteria with details, full plan, verdict); snapshot-tested compact and expanded.
|
|
236
|
+
- **Native ask-tool decisions (T075, owner directive)** — every decision point (needs_review, blocked, budget_limited, paused-after-verdict, complete, start-picker, amend-proposed, supersede-ack) ALSO asks through OpenCode's native in-composer form: the engine dispatches a decision prompt and the session's model asks the owner with the question tool (the only form-creation surface a server plugin has on 2.0.22), offering exactly the act-able choices (guidance rows marked as guidance). A form reply dispatches the SAME rpc act the TUI dialog carries; `decision.resolved` (via form/dialog) closes the other surface's dialog and a late stale reply is a no-op — answering either surface suppresses the other. The question-tool deferral keeps its engine-initiated exception during the ask window.
|
|
237
|
+
- **Fixed: "Decompose with CLEO" was a dead-end guidance row (T070)** — the complete decision's Decompose choice is a real act: it dispatches the CLEO decomposition prompt into the session (read the goal folder's summary/ledger, file each follow-up as a `cleo add --type task` with acceptance, link related tasks), and the dispatch lands in the ledger. Guidance-only rows are now marked `guidance: true` in the decision payload itself and rendered as advice, not buttons.
|
|
238
|
+
- **Completion states the loop stopped (T071)** — at complete/failed/aborted the card shows `■ loop stopped — follow-ups: .opencode/goals/<slug>/`, the composer banner says the loop has stopped and where follow-ups live, and the post-goal summary opens with the same statement (the digest keeps it on screen). Silence after complete no longer reads as broken.
|
|
239
|
+
- **Deferred /goal commands acknowledge themselves (T069)** — a `/goal` command typed while a turn or form holds the session acknowledges IMMEDIATELY (a notice naming what is queued and why) and executes the moment it ends — never silent. Control and decision-answer commands (status/list/help/pause/abort/approve/reject) stay immediate; deferring an approval behind the decision-form's own ask turn would be circular.
|
|
240
|
+
- **The panel walkthrough is pty-gated (T074)** — `scripts/tui-smoke.ts --assert` now extends the v0.3.1 seven with: after the run settles, the card shows the completing status (T072's live proof), the panel opens and cycles its tabs, and the Decisions tab renders its keyboard-selectable act rows. Spike S28 (the T074 finding): OpenCode 2.0.22 plugin keymap binds CANNOT join the leader chord — `leader+g`, `ctrl+x g` and bare `g` never dispatch while the palette route and direct keys work — which is why the owner had never seen the panel. The panel default bind is now the proven **F9** (`ctrl+p → "Goal: toggle dashboard"` also works); every affordance string says F9. Re-check on OpenCode upgrades: when plugin binds join the leader table, restore `leader+g`.
|
|
241
|
+
- **Fixed in the owner's live sign-off round (T074, named defects)** — the first reinstall exposed three misses, all fixed within the existing dashboard IA: (1) the panel now TAKES FOCUS on open, so `tab` cycles tabs and ↑↓/enter drive rows even when the composer was focused (with the composer holding focus, tab cycled nothing); (2) the Decisions tab is ALWAYS interactive — open decision rows first, then always-applicable quick acts (pause/resume/verify/approve/archive/start-next by state) in one keyboard-selectable list, never a dead text wall when no decision is open; (3) visual structure: the active tab is bracketed (`Now [Progress] Decisions Goals`), section headers carry rules, and the act list header names the keys. Gate-asserted as `panel.decisions-act-rows` (11 assertions total).
|
|
242
|
+
- **Housekeeping** — `.opencode/goals/.launch-approvals.json` (runtime state) is gitignored; the summary dialog digest points at the F9 panel.
|
|
243
|
+
|
|
244
|
+
## 0.3.1 — 2026-10-05
|
|
245
|
+
|
|
246
|
+
The launch-experience release: the palette is live on the real TUI, launches are clean and pickable, goals carry priority, decisions reach the owner as usable options. Built and proven under the goal loop itself (dogfood-4), with every live defect fixed behind a deterministic pty gate.
|
|
247
|
+
|
|
248
|
+
- **Fixed: the command palette listed zero Goal commands on the real TUI (T064)** — the keymap layer registered from the app slot defaulted to `mode:"base"`, and base-mode layers are unreachable while any modal is open (the command palette itself registers `mode:"modal`). The layer now ships `mode:"global"` like the host's own palette layer — proven by pty A/B, and gated ever since by `scripts/tui-smoke.ts --assert`, a deterministic pty capture that also asserts dialogs stay screen-fit and decision dialogs are keyboard-selectable (T065's live check, T066's render proof).
|
|
249
|
+
- **Fixed: goal_start refused legitimately approved launches (T068)** — launch approval matched the option label byte-exact (the bundled skill's own "(Recommended)" suffix broke it), lived only in memory (a server restart voided it), and the refusal could not say why. Approvals now match on the label prefix, persist to `.opencode/goals/.launch-approvals.json` (restart-durable inside the 10-minute window), and refusals name what was observed (no approval seen vs label mismatch vs expired).
|
|
250
|
+
- **Start clean (T059)** — `/goal start <slug> fresh` (and `goal_start({fresh:true})`) opens a NEW session owned by the goal, pins the run to it and prompts the kickoff there; the invoking session stays untouched (no clear-in-place API exists — none is pretended). The write-goal launch ask offers three launch options: Start goal now · Start in a fresh session · Start clean.
|
|
251
|
+
- **Start picker (T060)** — `/goal start` with no argument opens the startable-goal picker: a keyboard-selectable dialog (title + validation state) wired to the start path; with nothing startable it offers `/goal new`. The composer-completions spike verdict is recorded in spikes.md (S27): OpenCode 2.0.22 exposes no dynamic command-argument completions — the dialog is the surface.
|
|
252
|
+
- **Goal priority (T061)** — optional `priority: low|medium|high` in goal/v1, carried through the run state and the machine registry; `/goal list`, the palette goal lists and the start picker sort by priority then recency, absent-priority last. The write-goal interview asks for priority when the project already has a live goal.
|
|
253
|
+
- **Decisions leave a trail and keep their place (T066)** — every decision emission is ledgered (kind, message, choices, decisionId) and the owner's answering act pairs with it (`decision-resolved`). At a transition the decision dialog renders first; the summary queues behind it and renders as the screen-fit digest when the decision closes — the two dialogs no longer bury each other.
|
|
254
|
+
- **Complete offers more than Archive (T067)** — the complete decision gains a "Start the next goal" act that dispatches the write-goal interview into the session; guidance-only choices (the CLEO hints) render as visible rows in the dialog instead of being dropped; the needs_review copy states that approving triggers verification of everything else.
|
|
255
|
+
- **Verifier round budget (housekeeping)** — `verifierTimeoutMs` is now the PER-ROUND budget (240s default, up from total/2 = 120s per round — the last run's two provider hangs each burned a 120s round and retried too late), and stays overridable through plugin/project config.
|
|
256
|
+
- **T024 (the v0.3 epic) closed** — all 18 child acceptance criteria bound to their children's verified evidence chains; both finished dogfood runs (dogfood-buildout, ship-v03-feedback-loop) archived with history intact.
|
|
257
|
+
|
|
258
|
+
## 0.3.0 — 2026-10-05
|
|
259
|
+
|
|
260
|
+
The feedback-loop release: every decision persistent, actionable, visible. Built and proven under the goal loop itself (dogfood-3).
|
|
261
|
+
|
|
262
|
+
- **Fixed: the verifier child's silent empty exchange (T044)** — the root cause of all four dead verifier rounds on the main host. The child request now carries an explicitly resolved model (the parent session's live model, then the location default; recorded as a `verifier-model` ledger event), and an entirely empty exchange is retried once with an explicit last-chance prompt and always recorded (`verifier-empty` rounds, `empty after N round(s)` verdict detail) — never silent.
|
|
263
|
+
- **Post-goal summary (T045)** — on complete / needs_review / budget_limited the engine builds a summary from data the run already has: per-criterion outcomes with provenance (owner-approved and fallback-passed carry explicit caveats), the scope audit, restated non-goals, mid-run findings from the ledger, and follow-up prompts including the CLEO-install suggestion when the project has no `.cleo` workspace. Rendered as a `summary` rpc event + TUI dialog, a `goal.summary` transcript row, and a persisted evidence document.
|
|
264
|
+
- **Flagged completion scope audit (T046, owner decision)** — plan steps with `proves: []` and no recorded work, and `scope.in` lines mapping to no criterion, surface in the summary as *discussed but unproven*. Completion is never blocked by them (deliberately loose plans must not deadlock).
|
|
265
|
+
- **Owner-final beats host re-checks too (T057)** — the T051 fix covered the verifier only; the live C15 stomp came from a HOST re-check failing a stale needle after the owner approved it. Criteria passed `by: human` are now skipped by host re-verification as well and recorded as *approved by the owner (final)*.
|
|
266
|
+
- **Goals escape their session (T049)** — owner actions fall back to the project's unique non-terminal goal from any session (`/goal approve C4 [slug]`), and `/goal attach <slug>` moves a stopped goal into the current session (live goals wait for their turn to end). The RPC list carries attachable flags; the palette gains "Goal: attach/switch to a goal".
|
|
267
|
+
- **Decision dialogs at every decision point (T050)** — needs_review, paused-after-verdict, blocked, budget_limited, amend-proposed, supersede-ack and complete each emit a decision payload (kind, cause, structured choices); the TUI renders it as a selectable dialog wired to `rpc.act`. Nobody types an approval by hand.
|
|
268
|
+
- **Actionable message content (T054)** — one message registry: every engine notice carries the cause plus concrete choices, rendered inline for toasts and attached as structured choices for dialogs. A unit suite pins that no notice is actionless.
|
|
269
|
+
- **Persistent terminal feedback (T052)** — verdicts, owner actions, amendments, blocks, budget stops and completions land as transcript notice rows the agent sees next turn (queued rows deliver on the session's next turn), and an action-required state rides the sidebar view until the status resolves.
|
|
270
|
+
- **Live activity tracer (T053)** — the engine records what it is doing (turn, verifying, verifier child with its model, waits with countdowns, cooldowns); a pure formatter renders it with elapsed time above the composer and in the panel. Snapshot-tested; zero model cost.
|
|
271
|
+
- **Dashboard rework (T056)** — the sidebar card is capped at 12 lines (status, one bar, current step, next action, one action-required line — never a timeline); the panel is organized into Now / Progress / Decisions / Goals tabs with plain-language criteria and a C/I/S legend footer; decision rows are native Select components dispatching `rpc.act`; the panel opens on demand (`leader+g`, `tab` cycles) instead of displacing the sidebar; the raw ledger timeline is gone from the UI (it stays in the file).
|
|
272
|
+
- **Fixed: the dashboard freeze (T058)** — a dropped or late-attaching event stream could freeze the card at its first snapshot for a whole run (seen live). The TUI now re-fetches stale snapshots on renders and on a 4-second ticker for live goals; events remain the fast path.
|
|
273
|
+
- **Authoring rules against contract drift (T055)** — the write-goal skill forbids exact version-string needles and teaches prefix/semantic version checks, with a worked release-goal example (`startsWith`, changelog anchored on major.minor).
|
|
274
|
+
|
|
275
|
+
## 0.2.2 — 2026-10-04
|
|
276
|
+
|
|
277
|
+
- **Fixed: an owner-approved criterion is final.** Re-verification no longer re-runs the verifier child on a criterion the owner explicitly approved (`pass · by: human`) — previously a broken verifier could stomp the owner's sign-off, which is exactly what blocked the dogfood run's completion. The owner outranks the verifier.
|
|
278
|
+
- Filed from live dogfooding: T049 (goals are session-pinned — owner actions and palette commands dead outside the goal's session), T050 (human-in-the-loop dialogs at every decision point), and the palette/keymap investigation notes.
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
## 0.2.1 — 2026-10-04
|
|
282
|
+
|
|
283
|
+
- **Fixed: the TUI entry never loaded from git installs** (all versions). `@opentui/solid` and `solid-js` lived in devDependencies, and OpenCode's git installer ships production dependencies only — so the installed TUI entry crashed at load ("Cannot find package '@opentui/solid'"), killing the palette commands, the sidebar card, the dashboard panel and the footer pill while the server side kept working. Both are now regular dependencies; the server entry remains zero-dependency (types-only imports, unchanged). The git-install test now asserts the TUI runtime resolves in the installed cache. Found live by the owner after v0.2.0: dead palette and dead `/goal` dispatch (T047/T048).
|
|
284
|
+
|
|
285
|
+
## 0.2.0 — 2026-10-03
|
|
286
|
+
|
|
287
|
+
Built under the goal loop itself, with two council verdicts as decision records (`.cleo/council-runs/20261003T145323Z-a46b2ff9` and `…162655Z-800c52ca`).
|
|
288
|
+
|
|
289
|
+
- **Verifier reliability**: hardened prompt (exactly one `goal_verdict` call) with a sanctioned fenced-json fallback recorded as `by:"verifier-fallback"` and quote-re-read identically; child transcripts persisted under `evidence/` — a prose-answering or silent verifier can no longer kill a round.
|
|
290
|
+
- **Live counters**: `session.usage.updated` is per-message cumulative on 2.0.22; baselined at goal start with throttled persist+emit — `run.json` and the TUI no longer freeze at the kickoff snapshot.
|
|
291
|
+
- **Start-time rehearsal**: `/goal start` executes every command check once before locking, records the baseline, and refuses unrunnable check text (shell exit 126/127 or a `zsh:`/`bash:` diagnostic — never a tool's own stderr, so red bug-fix baselines stay legal).
|
|
292
|
+
- **Amendment**: `/goal amend` propose + `/goal amend confirm` re-locks an owner-edited contract with generation-bound records under `evidence/`, retaining prior evidence for unchanged criteria; the goal-folder write guard now covers stopped states; the integrity message names real paths.
|
|
293
|
+
- **Identity at scale**: descriptive slugs kept (hash/word names rejected by council); **supersession** (`supersedes: <slug>@<lock-prefix>`, terminal predecessor, archived, mid-flight admission rule requiring recorded owner acknowledgment); **archive tier** (`/goal archive` → `.opencode/goals-archive/`, demote-never-delete, registry keeps answering "was this ever a goal here?"); **admission probe** (exact existence + ranked title/intent similarity over live and archived goals, surfaced as owner options in `/goal new`).
|
|
294
|
+
- **Registry**: machine-level derived index (`~/.local/share/opencode/goal-registry.json`, `OCGOAL_REGISTRY` override), updated on persist with no-op skip and rebuild-by-scan parity; the scale benchmark (`spikes/scale-bench.ts`) shows folders stay fast at 5,000 goals (13.8 ms) while full list builds justify the index (157.6 ms).
|
|
295
|
+
- **Surfaces**: `/goal help` agent-forward guide from a pure builder; optional first-class CLEO linkage (CLI-only, `cleo-linked` ledger event, absent CLEO records nothing); TUI probes target the dashboard panel.
|
|
296
|
+
- **Coverage**: continuation notices are single synthetic rows; host scenarios for budget wrap-up, `goal_wait`, absent/diff, human approval, compaction survival, `session.deleted` and provider degradation; `parseContract` warns on multi-line command checks.
|
|
297
|
+
- Provenance: OpenCode store probe persisted (`docs/opencode-store-probe.md`); upstream CLEO reports #1804/#1805 (evidence-atom DX) and #1833 (a retraction with thanks).
|
|
298
|
+
|
|
299
|
+
## 0.1.0-alpha.2 — 2026-10-03
|
|
300
|
+
|
|
301
|
+
Second pre-release, dogfooded: built and shipped from inside the first real-model goal run (`docs/dogfood-1.md`).
|
|
302
|
+
|
|
303
|
+
- TUI: `session.panel` goal dashboard — contract title/status, budget cells, criteria board with per-criterion evidence, plan steps, latest verdict with its lines, and a ledger Timeline section. Opens automatically (once per run) whenever the session has a goal; `Goal: focus dashboard` palette command; re-render driven by GoalRpc `updated` events.
|
|
304
|
+
- RPC: `GoalView.timeline` — the last ledger events, summarized server-side (`src/server/app.ts` `timelineOf`), so the dashboard shows the run's history without file access.
|
|
305
|
+
- write-goal skill: `references/examples.md` with four complete goal/v1 contracts (bugfix, perf, migration, feature), each verified against `parseContract`; trigger evals run against a real agent (15/16, see `evals/trigger-eval-2026-10-03.json`); skill passes awesome-skills `validate.py`, `audit_body.py` and `check_depth.py`.
|
|
306
|
+
- Storage: `.opencode/goals/*/run.json`, `ledger.jsonl`, `evidence/` are gitignore candidates (HANDOFF §8.4).
|
|
307
|
+
- Dogfood findings: `docs/dogfood-1.md`; defects filed as CLEO T013–T015.
|
|
308
|
+
|
|
309
|
+
## 0.1.0-alpha.1 — 2026-10-02
|
|
310
|
+
|
|
311
|
+
First installable pre-release, for OpenCode 2.0.22+ only.
|
|
312
|
+
|
|
313
|
+
- Goal contract `goal/v1` (`.opencode/goals/<slug>/goal.yaml`): parser and validator with criteria, invariants, protected oracles, plan, assumptions, optional budgets, autonomy and verification settings.
|
|
314
|
+
- Engine: continuation on `session.execution.succeeded`, idempotent prompts with native-format ids, location filtering, stall detection by worktree change, blocker ×3, budget wrap-up, interrupt/error mapping, restart recovery (paused), contract rendered into every request (byte-stable system block + per-turn status note), compaction hook.
|
|
315
|
+
- Verification: host-run checks (command/file/contains/absent/diff) in the owner's login shell, integrity and protected-path checks, hidden read-only `goal-verifier` agent whose quotes the host re-reads, owner sign-off for `human` criteria; failures return a HOST VERDICT to the worker.
|
|
316
|
+
- Tools: `goal_status`, `goal_progress`, `goal_claim`, `goal_block`, `goal_flag`, `goal_wait`, `goal_validate`, `goal_start` (owner approval required), `goal_verdict` (verifier only).
|
|
317
|
+
- `/goal` command: `new`, `start`, `status`, `pause`, `resume`, `verify`, `abort`, `approve`, `reject`, `validate`, `list` — run as plugin code, no model turn.
|
|
318
|
+
- TUI: sidebar goal card, prompt-footer pill, composer banner when the owner is needed, toasts and desktop attention, palette commands.
|
|
319
|
+
- Bundled `write-goal` skill (v0.1, pending a skill-forge/skill-creator pass).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Keaton Hoskins (kryptobaseddev)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,142 @@
|
|
|
1
|
-
#
|
|
1
|
+
# opencode-goal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Persistent, host-verified goal mode for [OpenCode 2](https://opencode.ai). Describe what you want, let the bundled **write-goal** interview turn it into a structured contract, and the plugin keeps the session working until the host — not the model — proves every criterion.
|
|
4
|
+
|
|
5
|
+
> **Status: released (`v0.5.0`).** Engine, verification and TUI are built and tested end to end against OpenCode 2.0.22 with a scripted model, and each release is proven from its own installed git tag. CLEO integrates as an optional first-class citizen (`/goal cleo`). See [docs/HANDOFF.md](docs/HANDOFF.md).
|
|
6
|
+
|
|
7
|
+
## Why another goal plugin
|
|
8
|
+
|
|
9
|
+
Most goal loops let the model declare itself done. Research and user reports say that is the main failure: agents claim success that the files do not support. Here the worker can only **claim**; the plugin then runs the contract's checks itself, an independent read-only verifier must quote the files for anything a command cannot check, and the plugin re-reads those quotes. A failed claim goes back to the worker as a structured verdict and the loop continues.
|
|
10
|
+
|
|
11
|
+
| | |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Structured contract | `goal.yaml`: outcome, binary criteria with real checks, invariants, protected test files, non-goals, plan, assumptions, optional budget |
|
|
14
|
+
| Host-verified completion | command / file / contains / absent / diff checks run by the plugin; verifier with quote re-reading; owner sign-off criteria |
|
|
15
|
+
| Honest loop | progress measured by worktree change, stall and blocker detection, zero-token waits, budget wrap-up, Esc pauses |
|
|
16
|
+
| Compaction-proof | the contract is rendered into every request, never kept only in history |
|
|
17
|
+
| Live TUI | sidebar card, status pill, banner when you are needed, toasts and desktop notifications |
|
|
18
|
+
| No model turns for status | `/goal` commands run as plugin code |
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Requires OpenCode **2.0.22 or later** (OpenCode 1.x plugins and this plugin are not compatible). The package is **`@cleocode/goal`** ("CLEO Goal"); its bin is `cleo-goal`, with `ocg` ("open cleo goal") as an alias. The bin runs on plain Node 22.14+, no Bun needed.
|
|
23
|
+
|
|
24
|
+
Register the plugin for one project (writes only `<project>/.opencode/opencode.json`, key `plugins`; idempotent):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm i -g @cleocode/goal
|
|
28
|
+
cd your-project
|
|
29
|
+
cleo-goal install --project # --dry-run to preview, --json for a machine reply
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
With [CLEO](https://github.com/kryptobaseddev/cleo), `cleo goal install --harness opencode` runs the same step, and `cleo doctor goal-runtime` reports the installed version.
|
|
33
|
+
|
|
34
|
+
Or install it for every project through OpenCode itself (this writes the user-global config):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
opencode plugin add "@cleocode/goal@<version>"
|
|
38
|
+
opencode reload
|
|
39
|
+
opencode plugin list # opencode-goal should be listed
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Pin a version. A git tag still works as a spec: `github:kryptobaseddev/opencode-goal#v<version>`.
|
|
43
|
+
|
|
44
|
+
### The `cleo-goal` bin
|
|
45
|
+
|
|
46
|
+
| Command | Does |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `cleo-goal --version` | prints `cleo-goal <version>` |
|
|
49
|
+
| `cleo-goal status --json` | the live goal (else the newest finished one, else `status: "none"`) as JSON with `schemaVersion: 1`; exits 0 whenever the project was readable |
|
|
50
|
+
| `cleo-goal install --project [--spec <npm-or-git spec>] [--json] [--dry-run]` | adds this plugin to `.opencode/opencode.json`; replies `{schemaVersion, action: installed\|already-present\|updated\|dry-run, file, spec, plugins}`; exit 1 `{schemaVersion, error}`, exit 2 usage |
|
|
51
|
+
| `cleo-goal tick` | the Stop-hook tick CLEO's dispatcher runs: harness Stop JSON on stdin, JSON only on stdout |
|
|
52
|
+
| `cleo-goal claim --summary <text> [--evidence C1=<note> ...]` | records a claim from a harness with no OCG tools |
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
/goal new make the checkout test suite pass on Node 24
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The **write-goal** skill inspects the repository (commands, failing tests, baseline), asks you only the decisions it cannot look up — a few rounds of selectable options with a recommended answer — writes `.opencode/goals/<slug>/goal.yaml`, validates it, shows you the exact contract, and asks to start. Choose **Start goal now**.
|
|
61
|
+
|
|
62
|
+
Or write a contract yourself and run `/goal start <slug>`.
|
|
63
|
+
|
|
64
|
+
## The contract
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
schema: goal/v1
|
|
68
|
+
id: node24-checkout
|
|
69
|
+
title: Checkout suite passes on Node 24
|
|
70
|
+
intent:
|
|
71
|
+
verbatim: "make the checkout test suite pass on Node 24"
|
|
72
|
+
outcome: npm run test:checkout exits 0 under Node 24
|
|
73
|
+
non_goals: [Upgrading other packages]
|
|
74
|
+
criteria:
|
|
75
|
+
- id: C1
|
|
76
|
+
statement: WHEN the checkout suite runs on Node 24 THE suite SHALL exit 0
|
|
77
|
+
check: {kind: command, run: "npx -y node@24 node_modules/.bin/jest checkout", live: true}
|
|
78
|
+
- id: C2
|
|
79
|
+
statement: 'No deprecated Buffer() constructor calls remain in src/checkout'
|
|
80
|
+
check: {kind: absent, pattern: "new Buffer\\(", paths: [src/checkout]}
|
|
81
|
+
- id: C3
|
|
82
|
+
statement: MIGRATION.md explains each behaviour change
|
|
83
|
+
essential: false
|
|
84
|
+
check: {kind: verifier, ask: "Does MIGRATION.md explain every behaviour change made for Node 24?"}
|
|
85
|
+
invariants:
|
|
86
|
+
- id: I1
|
|
87
|
+
statement: The full suite SHALL CONTINUE TO pass on Node 22
|
|
88
|
+
check: {kind: command, run: "npm test"}
|
|
89
|
+
protect: ["test/**"]
|
|
90
|
+
plan:
|
|
91
|
+
- {id: S1, title: Reproduce the failures on Node 24}
|
|
92
|
+
- {id: S2, title: Fix them, proves: [C1, C2], depends_on: [S1]}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Check kinds: `command` (exit code / output expectations, timeout, repeated runs, `live` re-runs after file changes) · `file` · `contains` · `absent` (no match may remain) · `diff` (paths unchanged since start) · `verifier` (independent read-only judge that must quote files) · `human` (your sign-off). Optional: `scope`, `constraints`, `assumptions`, `budget {turns, wall, tokens, cost_usd}`, `stop`, `autonomy`, `verification {mode, max_rejections}`. Full reference: [skills/write-goal/references/goal-schema.md](skills/write-goal/references/goal-schema.md).
|
|
96
|
+
|
|
97
|
+
## Commands
|
|
98
|
+
|
|
99
|
+
| Command | Does |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `/goal new <words>` | run the write-goal interview (an admission probe surfaces existing/similar goals first) |
|
|
102
|
+
| `/goal start <slug>` [acknowledge-supersede] | rehearse every command check, then lock and run |
|
|
103
|
+
| `/goal` · `/goal status` | full status dialog |
|
|
104
|
+
| `/goal pause` · `/goal resume` | stop / continue the loop (resume resets the stall and failure counters) |
|
|
105
|
+
| `/goal verify` | run the completion checks now |
|
|
106
|
+
| `/goal amend` · `/goal amend confirm` | propose / re-lock an owner-edited contract (generation-bound audit trail) |
|
|
107
|
+
| `/goal archive` | demote a finished goal to `.opencode/goals-archive/` (never deleted; the registry still answers "was this ever a goal?") |
|
|
108
|
+
| `/goal approve C3` · `/goal reject C3 <why>` | sign off on a `human` criterion |
|
|
109
|
+
| `/goal abort` | stop the run for good (ledger and evidence stay) |
|
|
110
|
+
| `/goal validate <slug>` · `/goal list [all]` | check a contract · list live (+archived) goals; a machine-level registry also lists goals across projects |
|
|
111
|
+
| `/goal cleo` · `/goal cleo install\|init\|sync` | probe the CLEO CLI and this project's `.cleo` workspace (initialized / uninitialized / CLI absent); `install`/`init` run the exact documented command **only on your explicit choice** (never silent, never sudo); `sync [<slug>] T123[@sha] …` records genuine per-gate CLEO evidence and completes the linked tasks through the CLI |
|
|
112
|
+
| `/goal help` | the agent-forward guide: commands, tool split, storage, mid-run rules |
|
|
113
|
+
|
|
114
|
+
The same actions are in the command palette under **Goal**.
|
|
115
|
+
|
|
116
|
+
## What the model gets
|
|
117
|
+
|
|
118
|
+
Tools (only `goal_*`, visible by name): `goal_status`, `goal_progress` (step + one-line note + next action), `goal_claim` (evidence per criterion, then end the turn), `goal_block` (stable key; three in a row stops the goal), `goal_flag` (a criterion is contradictory, impossible or unsafe), `goal_wait` (resume after N seconds without spending tokens), `goal_validate`, `goal_inventory` (every contract in this project: drafts, live/stopped/complete, archives), `goal_manage` (pause/resume/verify/attach/abort/archive on an exact target), `goal_recover_divergence` (a disk contract that differs from its lock), `goal_propose_amendment` · `goal_resolve_amendment` · `goal_review` (staged contract changes and owner reviews), `goal_start` (only right after you chose **Start goal now**). The verifier alone has `goal_verdict`.
|
|
119
|
+
|
|
120
|
+
While a goal runs the model cannot edit the contract, files under `.opencode/goals/`, or protected paths, and questions to you are deferred (configurable per goal).
|
|
121
|
+
|
|
122
|
+
## Where things are kept
|
|
123
|
+
|
|
124
|
+
`.opencode/goals/<slug>/`: `goal.yaml` (yours), `context.md` and `decisions.jsonl` (from the interview), `run.json`, `ledger.jsonl` (every transition), `evidence/<run>/` (contract snapshot, rehearsal baseline, every verdict, verifier transcripts, amendment records). Finished goals live on in `.opencode/goals-archive/<slug>/` (demoted, never deleted). A machine-level index at `~/.local/share/opencode/goal-registry.json` (override with `OCGOAL_REGISTRY`) lists goals across projects and survives archiving. Consider ignoring `run.json`, `ledger.jsonl` and `evidence/` in git.
|
|
125
|
+
|
|
126
|
+
## Plugin options
|
|
127
|
+
|
|
128
|
+
Use the object form in your OpenCode config to change defaults:
|
|
129
|
+
|
|
130
|
+
```jsonc
|
|
131
|
+
{ "plugins": [{ "package": "github:kryptobaseddev/opencode-goal#v0.5.0", "options": { "stallTurns": 4, "cooldownMs": 3000 } }] }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`backstopTurns` (200, only when the contract has no turn budget) · `cooldownMs` (1500) · `stallTurns` (3) · `recoveryAt` (2) · `blockerRepeats` (3) · `maxPromptFailures` (3) · `verifierAgent` ("goal-verifier") · `verifierTimeoutMs` (240000) · `liveChecks` (true) · `launchApprovalMs` (600000).
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
`bun install && bun test` — the end-to-end suite starts an isolated `opencode serve` with a scripted model, so it never touches your config or sessions. See [AGENTS.md](AGENTS.md) and [docs/HANDOFF.md](docs/HANDOFF.md).
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|