Commit graph

55 commits

Author SHA1 Message Date
Pascal André
6a6367b560
chore(opencode): retire adaptations for runtimes and events OpenCode V2 no longer produces (#1034)
## Summary
This PR removes OpenCode compatibility code that nothing can reach any
more:
- Code that was already dead at the former 2.0.27 minimum, and is still
dead at the current 2.1.0 minimum (#1029).
- Handlers for events and data shapes that OpenCode V2 never produces.

Each item was checked against the upstream 2.0.27 and 2.1.0 sources
before removal.

Kept on purpose:
- Workarounds for upstream bugs or limits that are still present in
2.1.0.
- Code that reads conversations written by older runtimes: V1-migrated
tool names and metadata, `workspaceID` data, task summary titles,
`session-legacy` replay.
- Code that recognises and upgrades old CLIs and live older daemons: 1.x
replacement, beta recognition, 2.0.11 service discovery, 2.0.15 native
upgrade, `/api/status` and `/api/health` identification.

## Removed or changed
**Server**
- Negotiation: one "not modern → `canonical_api`" rule replaces the
pre-2.0.4 legacy schema recognition, and runs before #1029's
`native_contract` check.
- The transport no longer rewrites `/api/status`.
- Desktop skills no longer send the pre-2.0.4 `location` and `slash`
fields.
- The legacy `auth.json` usage fallback for daemons without
`credential.list` (pre-2.0.20) is removed, along with its UI reason and
the i18n key in 10 locales. A missing endpoint still fails safely.
- Workspace selectors are rejected up front, without a native resolution
request.
- CLI service probes try `/api/info` first. An admitted daemon no longer
costs two authenticated 404s on every check.

**UI**
- Remaining V1 `session.idle`/`session.status` branches: Missions store,
pending discovery (UI and server), `eventAffectsMessages`.
- `server.instance.disposed` and the now-unused `rehydrateInstance`.
- The V1 `worktree.ready` envelope (the handler now reads the V2 shape).
- V1 tool-state titles and the `success` status alias.
- Part `time.start` and `time.end`.
- Text stored as objects or segment arrays.
- `deriveMessageStatus`.
- The V1 `error.data.message` shape.
- `projectID` and `location` fallbacks on `session.created`.
- The replay-only `session.message.content.updated`.
- The unreachable `output.answers` fallback.

**Fixtures, Missions and docs**
- Legacy branches removed from the location, fork, families, pruning and
automation native fixtures. The WSL setup fixture now compares against
the reported version instead of 2.0.11.
- Unused 2.0.21 launch helpers removed.
- The Missions native and trajectory fixtures also run on the minimum
lane.
- The capability proof checks the reported version.
- Stale 2.0.2x labels now say "as of the 2.1.0 minimum".

**Follow-ups from the #1029 post-merge review**
- Test fixtures no longer send the ignored `recent: ""` compaction field
(18 sites).
- The manual compaction-isolation fixture accepts any published version
at or above the minimum.
- `CONTRIBUTING.md` wording updated.

114 files, +319 / −1117: source +90 / −487, tests/fixtures/CI +214 /
−604, i18n −10, docs +15 / −16.

## Validation (rebased on dev with #1029)
**Typechecks:** server and UI pass.

**Server tests**
- Touched areas: 480/480.
- Full server CI step: 2430 pass, 0 fail, 7 skipped, 1 cancelled. The
cancelled test is the `index.test.ts` real-process shutdown test; it hit
its 5 s timeout under load. It passes alone (4/4), and that file is
unchanged versus dev.

**UI unit tests**
- Touched files: 230/230.
- CI steps: 574/574, 200/200 and 519/519.

**Browser suites**, one at a time:
- Provider and service usage: provider-usage 13/13, service-usage 3/3.
- Missions: mission-visibility 3/3, mission-control-feedback 5/5,
mission-lifecycle-revision 10/10, mission-lifecycle-navigation 10/10,
mission-current-passage 1/1, mission-native-family 11/11.
- Transcript and rendering: compaction-responsiveness 5/5,
session-rendering 47/47, reasoning-collapse 3/3, render-cost 17/17,
tool-images 4/4, system-messages 10/10.
- Other: search-highlight 11 pass (1 skipped by its env gate),
panel-extensions 13/13.

**Native fixtures on an isolated 2.1.0 CLI:** location, session-fork,
automation, session-pruning, missions native, missions trajectory,
provider-usage, Missions capability proof and compaction-isolation all
pass. The WSL setup fixture was not run (it needs a WSL distro with a
Linux CLI).

## Left for later
- `RuntimeIdentity.discovery` is now write-only.
- `ownsLocation` has an unused `_client` parameter (about 30 callers).
- A test-only V1 translation in `auto-accept-manager.test.ts`.
- Out of scope: HTML-entity decoding (a product decision), the
`MutableHashMap` loop, the pruning-UI import-order workaround, unused
log-based Missions readers, and the unshipped managed-owner module with
its 2.0.24-pinned test.

Oversized source files touched: `stores/instances.ts` ~2,209,
`message-block.tsx` ~1,601, `workspaces/manager.ts` ~1,104,
`tool-call.css` ~1,047, `tool-call.tsx` ~865, `opencode-data.ts` ~865,
`api-types.ts` ~784, `session-events.ts` ~606,
`recurring-day-fixture.ts` ~535.
2026-10-20 19:19:47 +02:00
Pascal André
d6a890fba7
chore(opencode): qualify 2.1.0 and adopt its native contracts (#1029)
Adopt the OpenCode 2.1 native replay and compaction contracts, align client/plugin pins and set minimum and recommended runtime to 2.1.0. Preserve authenticated contract recognition for future/custom runtimes and explicit installation/restart boundaries.

Preserve historical conversations through official projection-based pruning revisions and independently checked migrations from 2.0.3, beta-19271 and 2.0.32. Retain ownership, execution claims, stale-selection checks and transaction fences. Update setup, help and qualification evidence.

Isolated Windows native qualification and integration checks cover pruning UI, historical reimport, Compare/Wayfinder, recurring Missions, builds and typechecks. Document broad-suite limitations and outstanding platform/package/mobile acceptance; this merge does not publish CodeNomad 1.0.0.
2026-10-20 16:46:38 +02:00
Pascal André
debbdce33f
feat(missions): add a "Compare two models" Mission template (#1026)
## Summary

Adds a fifth Mission template, **Compare two models** (`compare`). It
runs the same bounded task on two different models and ends with a blind
verdict.

- **Creation:** the user picks **model A** and **model B** in the main
creation form. Both are required and must name different models
(provider + model id; a reasoning variant alone is not a different
model). This is enforced on every creation path: HTTP, prepare,
recurrence create and agent-started missions.
- **Roles:** `model-a`, `model-b` and `comparator`.
  - A and B share one instruction text and report contract.
- The coordinator writes the task, inputs and criteria once, and gives
both byte-identical briefs.
- The server refuses a run task whose brief differs from its sibling's,
or whose per-task execution would change its role's model.
- **Blind judge, always on:** the comparator works on neutral X/Y
labels.
- It reports one versioned `comparison` artifact: the A/B task
references, per-criterion scores for both runs, sourced observations,
and a verdict `a | b | equivalent | inconclusive`.
  - Completion requires both runs and that comparison.
- **Judge model:** frozen once at creation, like every other role.
- An explicit judge is kept; otherwise the judge copies the
coordinator's effective model. When that model is unknown, both stay on
the native default.
  - Replays and retries therefore reuse the stored profile.
  - The editor warns when the judge's model equals A or B.
- **Readers:** the verdict, both model names, a score table and evidence
by path. Hashes stay under Technical details, for current and archived
recurring passages.

The template came out of evaluating Harness2's comparison loop. It
deliberately has **no** lesson, human approval, budget or duration
handling, and leaves the Wayfinder human gate unchanged. Up to five
models, per-run worktrees and launching from the composer's model picker
(OpenChamber-style multi-run) are left for later.

`AGENTS.md` records the single editor exception: the A/B selectors sit
outside the collapsed Options disclosure. All ten locales are updated.

## Size and upkeep

62 files, +837/-96:

| Part | Lines |
|---|---|
| Product code (about 16 files are one-line template-list hooks) |
+280/-57 |
| Locales (13 keys × 10) | +131 |
| Tests | +409 |
| Docs | +17 |

`missions/control.ts` stays oversized (~1,500 lines).

## Validation

- Full server suite: 2,426 tests pass, 7 existing skips. Root and server
typechecks, the server build and the plugin build pass.
- UI: 45 unit tests and 36 browser tests pass.
- The native trajectory fixture, extended with a compare case, passes on
OpenCode 2.0.31. It checks two distinct models, a one-newline brief
difference refused with the journal unchanged, a real blind judgment and
a completed mission. The native contract and recurring Q fixtures also
pass.
- Regressions cover:
  - distinct-model refusals on every creation path;
  - brief and override refusals;
  - artifact validation and the completion policy;
- judge freezing at creation, revision replay, and retry after a
coordinator model change;
  - editor inheritance and readable evidence.
- Independent review rounds ended with no P0-P3 findings.
- The editor and reader were checked from screenshots of the real Solid
components in the browser harness, not in a packaged app.
2026-10-20 13:55:12 +02:00
Pascal André
b2d5b722ec
feat(automation): complete safe native window interactions (#1018)
Complete application-window interaction on the shared page engine introduced
by #1015, while preserving native window/run and document-scoped ref authority.

Add real pointer clicks, hover, stepped pointer dragging, wheel scrolling,
keyboard chords and bounded text waits. Flat tool commands are validated at
both bridge boundaries. Clicks refuse covered targets; press(ref) refuses a
target that cannot acquire focus, so Enter never reaches an unrelated draft.
Drag requires both ends visible instead of scrolling under a measured source.
Preferences remains outside automation; focusing Preferences or a remote
window falls back to the most recently used local application window.

Trusted native input carries monotonic provenance intervals. Human Form
decisions check the originating event timestamp, not the time a stalled
renderer handles it; delayed keys and their implicit submit clicks remain
automated after lease expiry. Fixed page functions keep their depth marker.
Native events use the page's epoch clock, and cleanup preserves the original
press timestamp, independently of configured command timeouts.
Failed sequences best-effort release held buttons and keys, including when
the cleanup mark write itself times out. An admitted input timeout is reported
as an unknown outcome with inspection-before-retry guidance: the renderer may
deliver the input or finish the click later. Never replay these inputs.

Validation covers the actual Mission editor and InterruptionDock in isolated
Chromium, including a 16-second renderer freeze and a control human click.
Targeted server tests cover key mapping, both bridge parsers, focus/target
fences, failure cleanup and late input outcomes. Full server validation,
isolated OpenCode 2.0.31 automation, bundle regression and Tauri compilation
also pass; typecheck diagnostics remain at baseline. No installed application
or shared daemon was changed or restarted.

Four independent gatekeeper passes; the final review reports zero actionable
P0-P3 findings at 0ffb5a53b425500336854902c7068cd2233695f4.

Refs #1008.
2026-10-19 08:48:14 +02:00
Pascal André
1932da9307
feat(automation): shared page engine with budgeted outlines, select and replacing type (#1015)
codenomad.inspect/act could not fill real forms (#1008): type appended to a
prefilled field, native <select> could not be changed, and inspections were a
flat JSON list silently cut after 750 nodes.

Page automation now lives in a transport-agnostic engine under
packages/server/src/automation/page/, used by DeveloperCdp for the CodeNomad
window and designed to carry the browser preview next:

- snapshot.ts renders the accessibility tree as an indented plain-text outline
  with refs. Wrappers, unnamed images and text repeating a name computed from
  contents are dropped; fields show their value (numeric ones included) instead
  of inner nodes; native selects list options with selected/disabled. A
  32 000-character budget is shared among siblings: small parts stay whole,
  large ones collapse in place with "(+N hidden)" or keep their first and last
  items around "(+N more)". inspect accepts ref (one subtree) and query (exact
  role, or text in name/value/description, with contents and ancestors).
- refs.ts names a DOM node per target, run and navigation epoch; a ref keeps
  its name and stays usable for the whole page generation.
- actions.ts acts through fixed page functions whose inputs travel as protocol
  arguments. type refuses disabled fields and fields that cannot take focus
  (closed disclosure, modal dialog), so text never lands in another field; it
  replaces the field's text by default, clear: false appends (email/number
  through their value). Date/time/number/range/color values are validated
  before anything changes and keep the browser's normalization. select picks
  options by label, else value, dispatching input/change only on a change;
  clicking an option ref picks that exact option. Results return the field
  value or chosen labels; page exceptions are reported.

The trust model is unchanged and documented: in the CodeNomad window page
functions run under __codenomadAutomationDepth, clicks stay programmatic and
native text insertion yields only input events, so automation can never
produce the trusted dock interaction a human Form answer requires.

Validation: snapshot unit tests (exact outlines, query, scope, fair collapse,
budget property), updated controller/plugin/route tests, and a real-Chromium
regression driving DeveloperCdp over the DevTools endpoint, including the real
Mission editor (title replaced, chosen playbook reaches the created mission)
and every reviewed edge case. Full server suite green apart from a load-only
5 s timeout in index.test.ts that passes alone; isolated native automation
fixture passes on OpenCode 2.0.31. Three gatekeeper rounds; the last found no
P0-P3 issue.

Closes #1008.
2026-10-19 07:38:34 +02:00
Pascal André
b4f1dde4f8
feat(missions): coordinator hand-offs, task interrupts and native decision evidence (#1013)
Completes the coordinator frictions of #1009 left after #1011 (points 4 to 9)
and repairs the recurring Wayfinder human-mark journey checks.

mission.interrupt({ taskKey }) stops a retired native task's conversations
through the authenticated bridge: the coordinator's direct children whose first
user message carries that task's canonical assignment header. Each family is
interrupted deepest first, then the conversation, with lifecycle Stop's
post-interrupt recheck, settle and quiet verification; queued inputs are
cancelled under the worktree-deletion fence. It refuses while any open task
lives in that conversation's family (reuse or nesting chains, or its actor),
and never touches the coordinator, other tasks or the plan.

mission.prepare creates a paused one-time hand-off through the editor's
prepareMissionCreation pipeline, holds and fences, and never starts it. A
trusted preparedBy gives the preparing coordinator read-only visibility
(inspect({ missionID }), inspect({ list: true })), never control. Retries read
the journal's creation event first and are reported uncertain, never as
nothing created.

mission.inspect({ taskKey }) adds the task's observed native conversations as
advisory evidence; the task-key parser is shared with the UI. A native
Wayfinder decision readout may omit its artifact: the gate's discover() derives
candidates from exact native structure only and verifies each; only a complete
discovery with exactly one candidate fills it. requestID "auto" briefings may
let the server pick the revision. One replacement may merge several retired
tasks, and the task reader lists every predecessor. Scheduled passages, which
have no backend route, are not offered the new tools.

The Q and N journeys queried a retired human-marks key pattern; they now read
HUMAN_MARK_STORAGE_PREFIX, and the Q UI run exercises discovery natively.

Validation: the full server suite passes (2,383 tests, 7 existing skips).
Isolated native fixtures pass against a private OpenCode 2.0.31:
test-missions-native, trajectory, contract, family-control, and recurring
journeys ABCD and QN. The task-reader browser suite and bundle regression
pass; typechecks show only existing diagnostics. Three gatekeeper rounds; the
last found no P0-P3 issue.

Closes #1009.
2026-10-19 00:34:27 +02:00
Pascal André
1173beb37c
fix(missions): compact coordinator tool results and state the decision rule (#1011)
Coordinators driving a real Wayfinder mission hit three costly frictions
(#1009): every mission tool mutation echoed the whole map (about 50 KB after
ten tasks), the canonical assignment prompt only existed inside that
response, and the decision role read as a self-contained analysis although
completing it requires the human's own answer to a native question.

mission.delegate, revise, report and briefing now return a compact readout
(missions/tool-result.ts): disposition, current revision, status, run state,
frontier and the affected tasks, plus the contract and exact assignment
prompt for a declared native task. Briefs, reports and the objective stay
out; mission.inspect keeps the full map, and mission.inspect({ taskKey })
rereads one task with its contract and current prompt. The returned
revision feeds expectedRevision and basedOnRevision directly. Reports name
the task the control actually resolved, including an independent actor's
omitted taskKey.

The native Wayfinder decision role, the coordinator contract and native
decision declarations state the rule verifyHumanDecision enforces: the
task's own conversation asks through the native question tool and the user
answers in CodeNomad; self-contained analysis belongs to research.
Independent-mode coordinators, which that gate does not check, do not get
it. The refusal shows the exact provenance shape the gate validates, kept
aligned with nativeDecisionProvenance by a test. Refusals for a withdrawn
readout and unsettled withdrawn work explain what to do, a briefing citing
missing or retired tasks names them, and the Missions skill matches.

No journal, gate, ownership, RPC or UI contract changes. Interrupting a
retired task's native child, multi-task replacement, a paused-mission
creation tool, native execution visibility and Form IDs remain open.

Validation: 1,104 Missions server tests pass (one existing skip), with new
compact-result, task reread, retired readout, briefing source, implicit
report key, refusal shape and decision-rule tests. The isolated native
Missions, trajectory and contract fixtures pass against OpenCode 2.0.31.
Server typechecks show only the existing diagnostics. Two gatekeeper
rounds; the second found no P0-P3 issue.

Refs #1009.
2026-10-18 22:28:39 +02:00
Pascal André
ba0b8e5683
docs(sidecars): drop the rejected-isolation trace from agent guidance (#1004)
Documentation only. Removes the remaining "SideCar isolation was
evaluated and rejected" trace from `AGENTS.md` and the
architecture-guide `desktop-conventions.md`. Both keep the current
behavior: SideCars are trusted same-origin extensions, and cookie
filtering is not DOM/API isolation.
2026-10-18 03:49:32 +02:00
Pascal André
008e53aa27
docs(sidecars): keep rejected-isolation history out of the architecture notes (#1003)
Documentation only. Removes the 'isolation was evaluated and rejected'
sentence from \dev-docs/architecture.md\: the architecture notes
describe how SideCars work (trusted same-origin extensions), not the
alternatives tried. \AGENTS.md\ and the architecture-guide reference
kept a pointer to that sentence; they now state the reason directly, so
agents are still told not to reintroduce isolation.
2026-10-18 03:47:16 +02:00
Pascal André
1ce629b9a6
docs(sidecars): document SideCars as trusted same-origin extensions (#1001)
## Summary
Documentation only. SideCars are now documented as **trusted same-origin
extensions** of CodeNomad, replacing the "temporary exception /
isolation follow-up" wording introduced with #994.

## Why
The isolation follow-up was prototyped, measured and reviewed, then
rejected:
- **Opaque sandbox:** the frame loses cookies and storage, so the
service's own login and state break.
- **Separate origin:** keeping service cookies requires a same-site
origin (another port or subdomain). Chromium still sends the
application's `Lax` cookie across ports of the same host, and CORS alone
did not block authenticated writes, so every API would need an origin
barrier.
- **Per-view ports:** these are unreachable through a single exposed
port, reverse proxy or tunnel, and reset service storage on every
opening.
- **IP and single port:** with a literal IP and one exposed port, no
frame topology gives both isolation and working `Lax` cookies.

## Changes
- **`dev-docs/architecture.md`:** records the decision and these
constraints.
- **`AGENTS.md` and the architecture-guide `desktop-conventions.md`:**
refer to the decision instead of a pending prototype.
- **`CONTRIBUTING.md`:** no longer claims SideCar frames are sandboxed.
- **`README.md`:** tells users to add only services whose content they
trust.

The #995 cookie filtering stays as is; no behavior change.
2026-10-18 03:38:56 +02:00
Pascal André
be66133469
fix(sidecars): isolate application cookies from upstream services (#995)
Authenticate SideCar HTTP/WS requests before removing the configured CodeNomad session cookie, desktop session-cookie namespace and Remote Control device credential from upstream requests. Filter reserved response cookie names as well while retaining service cookies. Use the bounded Node HTTP parser for WebSocket handshakes so successful and rejected upgrades receive the same cookie treatment; preserve buffered bytes, binary forwarding and non-101 response framing. Document that this is credential filtering, not same-origin DOM/API isolation. Validation: 143 HTTP/proxy tests, 11 focused cookie/transport/preview tests, server typecheck, and isolated Chromium/Electron prototype exercises. General CI was still running at the explicitly authorized admin merge.
2026-10-18 00:28:05 +02:00
heunghingwan
d19d1c39fa
fix(sidecars): keep SideCar frames same-origin so proxied requests authenticate (#994)
## Summary

Since 0.20.0, opening a SideCar panel (e.g. ttyd) shows the page but
every in-page request fails: the terminal immediately falls back to
"Press ⏎ to Reconnect". All HTTP subresource fetches
(`/sidecars/<id>/token`, …) and the WebSocket upgrade
(`/sidecars/<id>/ws`) are rejected with 401/403. Connecting to the same
service directly (no iframe sandbox) works fine.

## Root cause

The browser-frame sandbox added in 0.20.0 (`allow-scripts allow-forms
allow-modals allow-popups allow-downloads`, no `allow-same-origin`) also
applies to SideCar frames. That makes the frame an **opaque origin**, so
the browser sends its in-page requests with `Origin: null`, cross-site,
and **without the `SameSite=Lax` session cookie**:

1. `sandbox` lacks `allow-same-origin` → iframe document is an opaque
origin
2. In-page fetch/WS requests become cross-site (`Origin: null`, no
cookies)
3. `codenomad_session` is `SameSite=Lax` → never attached
4. Server enforces session auth on `/sidecars/*` → **401**; the
opaque-origin gate additionally answers **403** (`http-server.ts`,
"Opaque origins may access preview capabilities only")

Only the frame's *subsequent* requests fail — the initial navigation is
issued by the top-level page (same-origin, with cookies), which is why
the panel visibly loads before disconnecting. Browser previews already
solve this with their token-scoped capability path; SideCars have no
such path and depend on the session cookie, so their frames must stay
same-origin.

## Change

Opt-in relaxation, scoped to user-configured app-proxied content only:

- `getBrowserFramePolicy(environment, { trustedSameOrigin })` appends
`allow-same-origin` to the sandbox only when explicitly requested
(`browser-frame-security.ts`)
- `BrowserFrame` gains a documented `trustedSameOrigin` prop; arbitrary
web previews must never set it
- `SideCarView` passes `trustedSameOrigin` — restoring pre-0.20 behavior
for content the user explicitly configured (a local service on
`127.0.0.1` reached through the app's own authenticated proxy)

Security posture is otherwise unchanged: the server-side gates stay
exactly as strict (the new server tests pin this), and browser preview
frames keep the full sandbox.

`allow-scripts` + `allow-same-origin` together do let SideCar content
script its same-origin parent — the same trust level SideCars had before
0.20.0 for services the user deliberately added. A follow-up can remove
even that by issuing short-lived tokens in the iframe URL (see below).

## Tests

- **Unit** (`browser-frame-security.test.ts`): trusted SideCar policy
includes `allow-same-origin`; existing tests still assert the default
policy excludes it
- **Browser** (`tests/browser/sidecar-sandbox.test.ts`, new): asserts in
a real Chromium DOM that the `SideCarView` iframe carries
`allow-same-origin` while a plain preview `BrowserFrame` does not
- **Server** (`server/__tests__/sidecar-proxy.test.ts`, new): WebSocket
upgrade **without cookie → 401**, **with valid session cookie → 101**
(also verifies `strip` prefixMode drops `/sidecars/:id` before
proxying); HTTP with `Origin: null` → 403, same-origin + cookie → 200 —
locking the exact contract that forces same-origin frames, so the server
gates cannot be quietly loosened instead

## Validation

- CI's runnable-UI unit list: 56/56 pass
- Targeted browser suite (`sidecar-sandbox` + full
`browser-frame-native` Electron suite) under xvfb: 20/20 pass
- Full server suite: 2286/2329 pass; all 27 failures are pre-existing
Git/gpg-signing environment failures (reproduced on an unmodified tree
via stash), none touch sidecars/auth/http-server
- `tsc --noEmit` clean for `@codenomad/ui` and the server package
- Manual: `ttyd -i 127.0.0.1 -p 7682 bash` as a port SideCar (insecure,
strip) — terminal stays connected; before the fix the token fetch was
403 and the WS handshake 401

## Follow-ups (not in this PR)

- Short-lived token in the SideCar iframe URL accepted by `/sidecars/*`
HTTP + WS auth, so even trusted sidecars could drop `allow-same-origin`
(mirrors the preview capability mechanism)
- Optional `SameSite=None; Secure` configuration for authenticated
sandboxed iframes behind HTTPS reverse proxies

---------

Co-authored-by: Pascal André <pascalandr@gmail.com>
2026-10-17 21:01:38 +02:00
Pascal André
db6685cb55
fix(workspaces): closing a project tab closes the project in CodeNomad (#970)
Some checks failed
Publish help / build (push) Has been cancelled
Publish help / deploy (push) Has been cancelled
## Problem

Closing a project tab only hid it in that window. The backend workspace
record kept routing the folder's events, notifications, auto-accept and
pending-request recovery, invisibly, until the palette-only **Stop
Workspace** command was run on the active tab. That command still showed
the V1 message "Stop OpenCode instance? This will stop the server."
although V2 has one shared OpenCode service, and it called
`debug.location.evict`, which (per OpenCode source) rejects pending
permissions, cancels pending Forms, fails plugin RPC and kills terminals
of that directory, even for sessions started elsewhere. OpenCode loads
locations on demand and unloads idle ones itself.

## Change

- **Closing a project tab** (×, Ctrl/Cmd+W) removes that tab's backend
workspace record. CodeNomad stops following it; sessions keep running in
OpenCode and reappear when the project is reopened.
- **Another tab of the same folder** has its own record and is
unaffected.
- **The same record shown in another window**: that window's tab closes
through the existing `workspace.stopped` broadcast, with an info
notification ("closed from another window"); the initiating window shows
nothing.
- **CodeNomad never evicts OpenCode locations**: eviction, its
serialization helpers and `locationOwned` bookkeeping are removed. A
failed launch now always removes its logical record (the retention only
existed to retry a failed eviction).
- **Stop Workspace removed** (command, confirm dialog, i18n keys in all
locales).
- Unchanged: opening an open project still offers a new tab (recent row)
or switching (switch button); temporary projects keep Keep / Discard /
Cancel; window close, app quit, reload, restore and backend shutdown
delete nothing; disconnected-instance dismissal stays local.
- Delete failure: error toast, record kept and reachable from the home
page, no automatic retry.

## Docs

Help in all 10 locales (`projects`, `index`, `desktop`, `server`,
`opencode`, `missions`), `AGENTS.md`, `CONTRIBUTING.md`,
`dev-docs/architecture.md`, `packages/server/README.md` and the
architecture-guide skill now describe the new contract.

## Validation

- UI store tests (`--conditions=browser`): app-tabs,
temporary-instances, workspace-list-reconciliation-fence,
instances-restore-ownership — 30/30.
- Server: manager + shared service 54/54; event ownership and workspace
identity suites pass.
- New real-component browser regression (click ×, Ctrl+W, Cmd+W): 3/3;
project restoration (Electron/Tauri) and new-instance launcher browser
suites pass.
- `npm run test:help` 9/9 (250 pages); UI, server and Electron
typechecks.
2026-10-15 15:14:29 +02:00
Pascal André
8e8ca15934
chore(opencode): qualify runtime 2.0.29 and align client/plugin pins (#948)
## Summary

- Recommend and pin OpenCode **2.0.29** (`@opencode/client`,
`@opencode/plugin`); the technical minimum stays **2.0.27** since
2.0.28/2.0.29 keep Effect 4.0.1.
- Add 2.0.29 as a reviewed contract profile beside 2.0.27; 2.0.28 and
future/custom labels keep authenticated negotiation.
- Test fixture only: the recurring Missions host declares the
`credential` domain that the 2.0.29 Promise adapter reads eagerly (the
managed owner spreads `ctx`, dropping the Proxy fallback). No product
code change.
- Two new isolated fixtures for upstream behavior changes: bounded
output-length continuation, and rejected Promise tools / native denial.
- Compatibility docs, CONTRIBUTING, AGENTS and architecture guide
updated.

## Upstream review

- OpenAPI: same 118 paths; additive schema changes (config `policies`,
provider/model `compatibility`, `encoding`).
- Private LocationServiceMap/Form/Permission/queue contracts used by the
pending snapshot: unchanged.
- Effect stays 4.0.1.

## Validation (Windows, isolated CLIs/databases, never the shared
daemon)

- 2.0.29: plugin runtime, automation, pruning/history/proxy, Missions
contract/family control/full journey, session environment, fork, aside,
prompt skills, Git degraded/init, provider usage/accounts, temporary
workspaces, blank-session cleanup, output-limit continuation, Promise
tool failure, system settings discovery, native upgrade 2.0.15→2.0.29.
- 2.0.27 (minimum): plugin runtime, automation, pruning, Missions
contract/family, session environment, fork, aside, prompt skills, blank
cleanup.
- Server: 2235 passed / 7 skipped / 0 failed. UI: 1524 passed. Tauri
crate 268/268, updater handoff 1/1, UI updater flow 13/13 (dev base
includes #786).

## Known, not caused by this change

`scripts/test-permission-receipts-native.mjs` times out identically on
2.0.27 with the previous pins; tracked separately.
2026-10-14 15:06:35 +02:00
Pascal André
af075a6fa5
feat(missions): add a Product delivery playbook based on NomadWorks (#946)
## Summary

Missions gains a fourth playbook, **Product delivery** (`delivery`; FR
"Livraison produit"), next to Flexible, Debugging and Exploration. Like
them, it is named after what it does. It adapts the NomadWorks
Collective method from the original
[NeuralNomadsAI/NomadWorks](https://github.com/NeuralNomadsAI/NomadWorks)
(`main` at `55eec7e`, v0.1.1). It works without installing that plugin.

- **Coordinator as Product Manager.**
- **Classification:** it classifies each request by complexity (tiny,
standard, complex) and track (implementation, investigation, spec).
- **Specification:** for behavioral work, it has the business analyst
and tech lead draft a Spec Change Request, then asks the human to
approve it through a native Form.
- **Delivery:** with the technical architect, it splits complex work
into slices (foundation, core, logic, ui, polish, qa, docs). Only one
task at a time edits the shared checkout.
- **Closure:** it closes only with QA and tech-lead evidence, and once
the documentation impact is resolved.
- **Six roles:** business analyst, technical architect, tech lead,
developer, QA engineer and UI/UX designer. Each returns the NomadWorks
specialist output sections: summary, work performed, acceptance-criteria
coverage, documentation impact, open risks and next step. Each role can
have its own agent and model in the profile selectors and in Missions
defaults.
- **Deliberate differences from NomadWorks:**
- Request approval is coordinator guidance, not a gate enforced by the
server.
- The Missions safety boundary replaces NomadWorks' agent commit
authority: the human owns commits.
- One playbook covers both NomadWorks team presets (mini and full),
because the coordinator declares only the roles each complexity needs.
- **Fix found along the way:** task-role validation checked every
non-Debugging playbook against the **Exploration** roles. A new
playbook's tasks would have been rejected. Each playbook now admits
exactly its own declared roles.

## Changes

- **Server:**
- the recipe in `missions/recipes.ts` and the roles in
`playbook-profiles.ts`;
- the `delivery` template id in every server, plugin, RPC and route
enum, and in the journal decoder;
  - the role check in `contracts.ts`.
- **UI:** the Playbook selector, the defaults-scope selector and
saved-brief validation.
- **i18n:** the playbook name and six role labels in all ten locales.
- **Docs:** `dev-docs/MISSIONS.md` (Included playbooks) and the
`codenomad-missions` skill.

## Validation

- Server and UI typecheck.
- All Missions server tests, including a new regression showing that
Product delivery admits exactly its own roles and that other playbooks
reject them.
- CI "Missions UI units" (36 files), plus every Mission and i18n UI unit
test (40 files). The locale-ownership test now covers 25 profile labels.
- `build:missions` plugin bundle.
- Browser tests `mission-defaults-models` and `mission-editor-profiles`
pass 24/24. The Flexible-exception loop now also covers the new
playbook.

Not yet validated: a live model run of a NomadWorks mission.
2026-10-14 14:45:38 +02:00
Pascal André
dd5a1f387d
fix(opencode): share host plugin runtime and require 2.0.27 (#920)
## Résultat
Corrige le chargement des plugins CodeNomad avec OpenCode 2.0.27 et
établit cette version comme minimum pour les prochaines releases et les
prereleases dev. Aucun déploiement de l’application active ni
redémarrage du daemon partagé.

## Cause démontrée
- Le runtime natif passe d’Effect 4.0.0-rc.112 à 4.0.1. Le bundle
précédent reproduit `cache.stackFrame` sur une CLI 2.0.27 isolée.
- Embarquer Effect 4.0.1 ne préserve pas la 2.0.26 : échec inverse
`cache.scheduler`.
- Les schémas 2.0.27 consomment `Schema.isStartingWith`, absent du
moteur 2.0.26. Le plancher est fondé sur cette rupture mesurée, pas sur
une liste de versions testées.

## Implémentation
- Pins client/plugin synchronisés à 2.0.27 ; Effect et le SDK public des
plugins sont fournis par OpenCode, hors bundles natifs
pruning/Missions/automation.
- Réglages de build partagés et régression qui interdit une seconde
copie du runtime ; smoke natif ajouté à la matrice minimum/latest.
- Diagnostic `plugin_runtime` traduit dans les dix locales ; versions
futures et personnalisées conservent la reconnaissance authentifiée du
contrat.
- Adaptations du SDK : annulation des lectures projet, références de
credentials externes et connexion fournisseur externe avec formulaire
natif, sans fallback OAuth/API key ni rejeu.
- Le runtime OpenTunnel du backend reste isolé et inchangé. Aucun
tunnel, route ou identité distant n’est modifié.

## Validation locale
- Échec reproduit sur 2.0.26 et réussite sur 2.0.27 pour les variantes
de bundle comparées.
- Suites natives isolées automation,
pruning/historique/proxy/concurrence/redémarrage et Missions réussies
sur 2.0.27.
- Régressions ciblées de build, store/serveur et navigateur (diagnostic
du minimum, connexion externe et brouillon conservé) réussies ;
typechecks UI/serveur/Electron/OpenTunnel réussis.
- Suite serveur complète et CI multiplateforme encore en validation à
l’ouverture ; aucun résultat entièrement vert anticipé.

La revue gatekeeper indépendante sera publiée ici avant fusion. Les
anciennes preuves de qualification restent historiques, pas une promesse
de support des moteurs antérieurs au nouveau plancher.
2026-10-12 16:42:07 +02:00
Pascal André
81fb513558
feat(missions): native project coordination, briefings and tracking (#866)
# Missions: product specification

## Product goal
Missions coordinates complex work through native OpenCode conversations.
CodeNomad provides creation, controls, human attention and result
reading; OpenCode owns execution. One-time objectives and finite daily
recurring passages share the same planning and reading experience. No
OpenCode modification is required.

## Create and configure
- **Flexible mission**: free-form objectives and bounded delegation.
- **Debugging**: diagnosis, implementation, independent
standards/specification reviews and fresh validation.
- **Exploration**: bounded decisions and parallel investigation, with
explicit native human questions where required.

Creation presents an objective, a short optional title, verbatim working
notes, coordinator/task profiles and native or independent task-session
policy. Optional choices stay collapsed. A single **Create** button
saves a prepared one-time mission or paused recurring schedule without
starting anything. **Play** is a separate explicit action for both.

Settings → Missions contains defaults for **all new missions**, with
optional exceptions for each of the three types, including Flexible. A
type exception overrides the global value; an unset field shows the
inherited value. Defaults never reconfigure existing missions or busy
actors. Reusable briefs contain creation inputs only; loading copies
them into a normal draft and deletion preserves executions. **Save as
brief** is also available from a selected one-time mission, including a
completed mission: it copies objective, verbatim notes, template, stored
profile choices and task-session policy; the mission title prefills the
brief name, never copying execution identities or results.

Profiles choose an agent, model and optional reasoning variant.
Coordinators use primary/all profiles; native task actors use
subagent/all and independent actors use primary/all. Business roles
remain separate from native session identities. Actor continuation
preserves the accepted profile.

The native subagent-depth editor is separately Location-scoped and
applies to all OpenCode conversations there, not just missions. It shows
the configuration file, effective value and override. Conditional Save
preserves the original file expectation and never automatically reloads
or restarts OpenCode.

## Missions panel
The first toolbar holds only general actions: **Create**, **Settings**
and **Refresh**, aligned right. Settings opens the Missions settings
section directly, using the existing native Preferences window on
desktop. A native refusal shows an error rather than opening a second
embedded window; browser and remote windows retain their embedded
settings.

The current-mission line contains a leading chevron, the selected title
and a small status mark:
- Clicking the **chevron** expands a persistent inline list; selection
leaves it open.
- Clicking the **title** opens a transient popup; selection, outside
interaction or Escape closes it.
- Both lists show titles and status marks, with full labels in tooltips
and accessible text. Titles truncate only at the available row edge.
- Search appears only when the measured list exceeds its maximum height,
and remains available while a query is present.
- The closed chevron indicates activity/attention in other missions
using the same visual vocabulary as the session list.
- Only the picker changes the selection, remembered per project; opening
a mission's conversation never reselects a mission.

Selected-mission controls are separate: **Play/Pause**, **Stop**,
optional recurring **Run now**, then **Summary**, **Open coordinator
conversation**, **Edit** and **Delete**. Destructive actions require
confirmation. **Request an update** is a text button below the icons,
available only while meaningful; its pending/uncertain state is retained
without automatic replay.

The **Tasks** disclosure contains only the declared dependency tree, not
a duplicate task list. It is initially open, remembers its state and
uses the remaining panel height down to the window bottom rather than a
fixed task-height cap. Rows retain full-width selected/hover backgrounds
across connectors and controls. A task's exact conversation shortcut
appears on desktop hover/keyboard focus, and remains visible on touch.

Mission selection is remembered per project and changes only through
explicit mission selection/creation. Opening a coordinator or task
conversation in the session list never changes the selected mission.
Conversation shortcuts remain available from the mission panel to the
coordinator and exact task conversations. The session-list files are
unchanged relative to `dev`; restoring the full cached session hierarchy
is supplied by the separately merged #841.

## Plan and native execution
The coordinator declares tasks, dependencies, roles and expected
outcomes. Ready work can run in parallel through ordinary native
subagents. Recursive decomposition follows OpenCode permissions and
configured depth; the product imposes no delegation target or root
fallback.

Independent sessions remain available for an explicit Location, lifetime
or existing-root requirement. The task-session policy is frozen per
mission. Tasks have stable keys/generations; conversations have exact
native identities. Business dependency edges never stand in for native
ancestry.

Native task returns and business settlement are separate: a coordinator
records the actual returned result/evidence, then finalizes the mission.
A notification/admission acknowledgement is not proof of consumption or
completion.

## Results and human interaction
Long objectives, briefings, task results, evidence and plan history open
in the central reader above the transcript. Reader eyes toggle the exact
target and highlight it. Task results come before the original brief and
technical details. A missing/unavailable reader target must not leave
the transcript hidden.

The coordinator publishes dated, bounded briefings explaining delivered
results, remaining work, obstacles and the next step in plain language.
Briefings remain separate from task settlement and observed activity;
exact request identity is required to acknowledge an explicitly
requested update.

Attention contains real native Forms and permissions. Obstacles reported
in prose are not automatically human requests. Native interruptions
retain conversation provenance and drafts in the shared interruption
dock. A template's required human decision counts only when answered
through the CodeNomad UI, locally or from a live paired Remote Control
device; a simple UI-answer mark records that provenance.

Users give direction or ask questions by writing in the coordinator
conversation (**Open coordinator conversation**). From the panel itself,
the only coordinator message is **Request an update**; its
pending/uncertain state is retained by identity and never replayed
automatically.

Reports, plan history and cleanup receipts stay secondary in
reader/technical surfaces. Optional managed-session cleanup preserves
coordinators, shared/reused/moved sessions and roots with descendants.

## Lifecycle and recovery
For one-time missions:
- **Play** starts/resumes explicit mission intent; **Pause** parks
mission dispatch/notifications and interrupts the observed active native
family.
- **Stop** is terminal and interrupts observed descendants
deepest-first, then registered roots/coordinator, with bounded rechecks
for parent wakes caused by child cancellation.
- Native inbox cancellation uses exact authorized mission/subagent work.
Pause preserves deferred results for explicit Play; Stop removes the
identified queued work/results that would wake a stopped coordinator.
- Per-target receipts record observed interruption and family coverage.
Incomplete or unconfirmed interruption is shown as **Some sub-agents may
still be running**, never as proven recursive suspension.
- **Refresh** reconciles state first; an explicit refresh click may
retry a still-unconfirmed lifecycle action under its original identity.
Reconnect and ordinary display reads never resend mutations.
- **Play** offers targeted recovery only when native observation
establishes the applicable interrupted state, not merely because
activity is unknown. Active background descendants retain the
active/Pause presentation.

Native interruption does not undo completed filesystem/tool effects.
Resuming a paused mission lets the coordinator decide how to continue or
re-delegate interrupted work; there is no hidden task replay engine.

## Recurring missions
Recurring setup adds permanent instructions, a title, a daily local
time/timezone and explicitly followed project conversations. Saving
creates a paused schedule; Play arms it and Run now requests one finite
passage.

The OpenCode service/plugin owns scheduled passages while both CodeNomad
UI and its intermediary backend are closed. One native Job per running
schedule sleeps until the earlier of the next due time and one hour. On
waking it rereads the clock/document and admits only the latest due
civil day newer than the stored daily cursor; there is no backlog.
Native execution terminal events also wake observation after a debounce,
but are not settlement evidence.

Before native effects, conditional storage records deterministic
passage, coordinator and start-message identities. Lost replies
reconcile those exact identities rather than creating new attempts. A
passage archives only after the coordinator and descendants are
inactive, inboxes are empty and no relevant native Form, permission,
Shell or background subagent remains. Unknown coverage stays pending.

Passages end as completed with a final report, native terminal failed,
or ended-without-report/interrupted. Only completed passages advance
followed-conversation cursors. Schedule history is bounded to 30
passages. Civil-time DST gaps/repeated times and entirely skipped days
resolve deterministically.

Pause/Stop cancel the schedule Job; those schedule actions do not prove
an already-running passage family stopped. Stopped is terminal. After
service restart, a desired-running schedule without its Job is
**Interrupted**, awaiting explicit Resume. Resume reconciles pending
work, never sends a continuation; a turn cut by restart settles
interrupted/ended-without-report.

## Durability and UX
Mission journals and schedule documents retain tasks, roles/profiles,
results, plan revisions, controls, notifications and bounded history.
Conditional writes and original request identities protect mutation
retry. Authority, Location ownership, fresh per-send environment and
connection/deletion fences are checked at admission. Ordinary native
permissions govern tool use.

UI remains compact and square, localized in all supported languages,
keyboard-accessible and usable on touch/narrow/RTL layouts. Cache-first
display reads revalidate on visible demand and native invalidation,
without polling or clearing known data on uncertain recovery. Loading,
missing capability and uncertain effects remain explicit.

## Shared integration scope
The PR also includes supporting shared-runtime corrections; these are
not limited to the Missions tab:
- Session outcome and generation-recovery reconciliation retains
uncertain interruption state across reconnects and checks native
outcomes before clearing it. Native message pages clamp to the service's
200-message limit.
- Pending-request recovery retains known requests through incomplete
coverage and connection transitions; native ownership and deletion
fences remain authoritative.
- Keyboard offset and the compact composer help strip preserve reachable
input controls without changing composer height limits. Touch overflow
actions suppress duplicate compatibility clicks.
- Workspace text saves enter the existing worktree mutation fence;
sandboxed panel asset reads fence connection changes. Worktree scans
retain validated family identity and custody.
- The worker-based Git process runner uses an ESM-safe worker entry, and
CLI service discovery shares bounded command deadlines.
- Desktop/backend profile identity is explicit for Missions' owned
storage. Profile-scoping signals are internal to that relationship, not
permission for independently launched applications to inherit a profile.
- Shared rendering/localization includes a Turkish tool-call label
correction.
- Session-list startup restoration exposes a settled signal
(`stores/session-list-restoration.ts`, hooked in `session-api.ts` and
`instances.ts`). Missions display reads wait for it so the session list
loads first; it settles on success, failure, timeout or abandonment and
is reset when a workspace is removed. Session-list components and styles
are unchanged.

These callers are included in integration review and their regression
tests, rather than treated as an unchanged dependency surface.

## Current limitations
- Editing/deleting recurring schedules is not implemented; those icons
in the selected mission's action bar are disabled for schedules.
- An uncertain one-time creation (lost reply after the request was sent)
is held in window memory with no status endpoint; Create stays blocked
for that window until reload. Definitive refusals before any native
effect re-enable Create immediately.
- Saving a brief from an existing recurring schedule is not implemented;
the selected-mission action currently covers one-time missions only.
- Task conversation lookup for older unbound native tasks reads a
bounded coordinator-message window and recognized exact assignment
evidence. Older/unrecognized delegations may have no shortcut; no
conversation is guessed from titles or timings.
- Running native calls may not expose their child ID until completion.
The panel can instead identify a direct child by its first canonical
assignment message, within a bounded direct-child page; unrecognized or
out-of-bound children remain unlinked.
- Native task profiles are recorded, but ordinary subagent launch still
relies on the coordinator passing the declared agent/model/variant to
OpenCode. Omitted model parameters can inherit the coordinator's model;
profile storage alone does not enforce launch parameters.
- One-time recursive interruption is best-effort and bounded, not a
durable guarantee that every descendant stays stopped. Partial/unknown
coverage remains visible.
- Recurring Pause/Stop cancel scheduling, not guaranteed recursive
suspension of a running passage.
- Native qualification uses isolated services; mock-only and
source-review evidence never substitute for real native acceptance.
Final validation results are published separately in the PR reviews.
- Managed-service restart and some
lost-reply/clock-conflict/human-decision journeys still have
offline-only evidence.
- If an admitted passage start is removed by pruning before its lost
durable write is reconciled, that original start identity can become
unavailable for deduplication.

## References
- [Mission
contract](https://github.com/NeuralNomadsAI/CodeNomad/blob/experiment/missions-native-subsessions-20261003/dev-docs/MISSIONS.md)
- [Recurring
contract](https://github.com/NeuralNomadsAI/CodeNomad/blob/experiment/missions-native-subsessions-20261003/dev-docs/MISSIONS_RECURRING_SIMPLE.md)
- [Native
qualification](https://github.com/NeuralNomadsAI/CodeNomad/blob/experiment/missions-native-subsessions-20261003/dev-docs/MISSIONS_RECURRING_SIMPLE_QUALIFICATION.md)
2026-10-11 14:55:49 +02:00
Pascal André
8484cf0a57
fix(desktop): separate the update feed from the local data profile (#891)
# Separate update feed from desktop data profile

## Problem

A single "channel" value currently drives two unrelated things on
desktop:

- which releases CodeNomad offers as updates;
- which local data profile (tabs, windows, drafts, WebView storage,
per-profile backend) is opened.

The channel is inferred from the installed version string (`-dev-…` →
`dev`, otherwise `stable`) or from `CODENOMAD_UPDATE_CHANNEL`.
Installing a `0.20.1` build over a `0.20.1-dev-…` build therefore
silently reopened a different, empty profile: same application, same
OpenCode sessions, but previous tabs and drafts gone from view. Users
had no setting to choose updates and no indication of which profile was
active. The Windows update action always targeted the stable winget
package, and `dev-v2` builds received no prerelease notifications.

## Expected behavior

**Normal users** install or update CodeNomad and get their projects,
tabs and drafts back. The installed version never selects a data
location.

**Update feed** is a visible user preference in Settings → Info →
Updates:
- *Stable releases*;
- *Previews and stable releases*.

When unset, builds labelled `-dev-*` default to previews, others to
stable. Changing the feed never touches local data. Windows runs winget
only for the stable feed; previews open the release page.

**Data profile** is explicit developer isolation:
- packaged builds use the normal default profile;
- `CODENOMAD_PROFILE=<name>` opens an isolated named profile, using the
existing scope formula so existing folders remain reachable;
- `CODENOMAD_UPDATE_CHANNEL` remains a deprecated alias for the profile
only, mapping existing values to exactly the same folders;
- unpackaged development runs keep their own `dev` profile;
- Electron and Tauri normalize and validate names identically; invalid
explicit names fail visibly.

Settings → Info shows the active profile only when it is not the
default.

**Startup restore settings are reachable on desktop.** Desktop Settings
open in the separate Preferences window, which previously hid the
Startup card, so *Restore previous state* and *Clear saved state* were
unreachable. Preferences now shows them. Each change applies to the
exact main window that opened Preferences, through that window's
existing saved-state handling; Preferences never becomes a client-state
writer. If the opener is gone, the card stays visible, disabled and
explained.

**One-time transition** for existing installs without an explicit
profile: CodeNomad checks, read-only, which of the default and
previously version-selected profiles (`dev`, `dev-v2`) for the same
configuration hold restorable state. One → use it; none → default;
several → ask once with a native dialog before any profile is opened.
The answer is remembered by profile name, never by path. Nothing is
moved, merged or deleted.

OpenCode sessions remain shared across profiles.

**Other saved profiles can be deleted.** When other data profiles exist
on the machine, Settings → General → Startup shows one row — *Other
saved profiles: N profiles, X GB — Delete…* — hidden otherwise and in
plain browsers. The confirmation lists exact names and sizes and states
that OpenCode conversations are unaffected while those profiles' tabs,
drafts, layout and web storage are deleted. Profiles in use (or of
unknown state) by either host are skipped and reported. The renderer
sends only listed IDs; the host re-lists, re-validates and deletes only
paths a profile provably owns. The default profile's storage contains
other roots, so only its own known children are removed, never a parent
root. The open configuration's remembered choice is dropped when it
names a profile deleted for that configuration; other configurations'
entries are dropped only once no folder of that profile remains. At
launch, a remembered named profile whose folder is definitely gone
reruns detection instead of opening an empty profile; unreadable folders
keep the choice. If the profile folder is a link whose target is
unavailable (for example a disconnected drive), detection decides that
launch only and the remembered choice is kept for when the target
returns.

## Out of scope

- Moving existing profile data to the standard location.

## Documentation

A developer document explains installed version, update feed and data
profile as three separate notions, with precedence, the legacy alias and
the transition.

## Implementation choices

- Both hosts resolve the profile before the singleton, storage folders
or client-state path are fixed. Grammar for `CODENOMAD_PROFILE`: after
trimming ASCII whitespace and lowercasing ASCII letters, 1–64 characters
from `a-z 0-9 . _ -`, starting with a letter or digit; `default` and
`stable` mean the default profile. The legacy alias keeps each host's
previous normalization for exact folder parity.
- A store counts as restorable only if a window with restore enabled has
at least one workspace tab, read with the existing client-state parsers.
- The remembered choice lives in
`<appData>/CodeNomad/profile-selection/choices.json`, keyed by a hash of
the config path, storing only the profile name. Writes are atomic;
corrupt entries rerun detection; concurrent first launches serialize on
a lock (stale after 15 s) so only one asks.
- Tauri asks synchronously with `rfd` before the builder. Electron asks
from a throwaway temporary folder, records the answer and relaunches,
because Chromium storage and the singleton are fixed before the app is
ready.
- The backend receives the resolved profile as
`CODENOMAD_DESKTOP_PROFILE`.

## Known limitations

- The transition question is English-only: no native localization exists
before a profile (and its saved language) is open.
- With three candidate profiles the native dialog has no separate Quit
button (`rfd` allows three buttons). Closing it quits on Windows and
Linux; on macOS the user must pick a profile, which moves nothing.
- Two launches racing on a stale lock may both ask; the first remembered
answer wins.
- On macOS, Tauri profiles share one WebKit data store, so deleting
other profiles keeps that shared web storage (sign-ins, cache); the
confirmation says so on macOS.
- Scope hashes keep each host's historical case folding so existing
folders do not move; only the new remembered-choice key uses a shared
Unicode fold on Windows.
- Real native dialogs and Electron's relaunch path are covered by unit
tests, not exercised interactively on every OS.

## Validation

- Shared cross-host vectors (`data-profile-vectors.json`): legacy scope
hashes, grammar, precedence, client-state fixtures.
- Electron full `test:native`: 273/278 pass, 4 POSIX-only skips; the one
failure, `process-stop.test.ts` (unchanged from `dev`, PowerShell under
a 5 s timeout), is flaky on the Windows test machine and passes when
rerun.
- Tauri full `cargo test`: 245/246 pass; the one failure,
`cli_service_lifetime_tests` (unchanged from `dev`, Node fixture with a
5 s deadline), passes when rerun. `cargo check` clean apart from
pre-existing dead-code warnings.
- UI browser tests for other profiles, Preferences startup state,
Preferences window and Settings → Info: 19/19. Server `desktop-profile`
and update-feed tests pass.
- Electron, server and UI typechecks clean.
- Covered cases: none/one/several restorable profiles, remembered and
corrupt/newer choices, keying by config, stale and live locks, a
concurrent launch waiting for the answer, and that the installed version
is not an input.
- Developer documentation: `dev-docs/DESKTOP_DATA_PROFILES.md`.
2026-10-09 18:55:14 +02:00
Pascal André
9dd4b3f059
feat(remote-control): add Remote Control over OpenTunnel alongside direct access (#668)
Adds Remote Control alongside direct/LAN access. A phone or another computer pairs with CodeNomad and continues its sessions from anywhere, without an inbound port. The transport is OpenTunnel, the blind TLS relay used by OpenCode: TLS ends inside the CodeNomad backend, and nothing has to be deployed.

- packages/remote-tunnel wraps @opentunnel/client. The server build bundles it with its own Effect 4.0.x, so the published server has no runtime dependency on a private workspace package. CodeNomad claims its own route on the device's shared identity and never deletes it.
- A remote-only loopback ingress marks its sockets as remote, so local privileges come from isLocalRequest(), never the peer address. The gate runs before CORS, auth and routing:
  - exact tunnel Host, and the public Origin on mutations;
  - stripped forwarding headers;
  - origin-form targets only, judged as routed (decoded, cut at ';');
  - host-only paths denied;
  - a paired device required, except for the public UI bundle.
- Host-owned pairing: a single-use five-minute code, hashed device credentials with 30-day idle expiry, and a __Host- cookie. Revocation or eviction closes live streams. A revoked device is asked to pair again, keeping drafts.
- The Cloudflare Worker/Durable Object relay, the custom E2EE protocol and the browser transport shims are removed. Electron and Tauri keep the backend alive after the last window only while Remote Control is enabled.

Validation:
- the real createHttpServer is exercised through the ingress, plus raw-socket canonicalization tests and device registry tests;
- recovery unit and browser tests;
- an opt-in end-to-end run through the hosted relay;
- a packaged smoke import of the bundled runtime;
- five gatekeeper rounds to zero findings, and green CI on all desktop targets.
2026-10-09 12:43:25 +02:00
Pascal André
c995044b64
feat(extensions): install independently distributed right-panel addons (#862)
## What changes

I add independently distributed right-panel UI extensions and an
official online catalogue in Customize right panel. Users browse/search
available addons, choose one and confirm installation directly from its
exact GitHub Release, without a manual ZIP download, rebuilding
CodeNomad or restarting OpenCode. Manual ZIP installation remains
available.

I publish the curated index, contribution rules and Session info demo at
https://github.com/NeuralNomadsAI/CodeNomad-Extensions. That repository
can host several official addons; the index can also reference
author-owned repositories. Separate means independent of the main
application repository, not a mandatory repository per addon.

I show the manifest and exact archive SHA-256 before trust confirmation.
Installation starts disabled; users enable it for all projects in the
server profile or for the exact opened folder. Replacement requires the
previous digest and revokes every activation grant. Removal is explicit.
Packages and grants stay outside project/OpenCode storage.

I keep author code out of the main renderer. API 1 exposes only session
ID, locale and appearance through a per-mount MessageChannel. It does
not expose filesystem, transcript, images, credentials, native commands
or the OpenCode proxy. Frames are sandboxed without same-origin access
and receive a restrictive CSP; obsolete responses and ports are fenced
on project/session changes, disconnects, revocation and replacement.

I document the package, permissions, API compatibility, scope,
release/checksum and maintainer rules in `dev-docs/PANEL_EXTENSIONS.md`,
with a standalone starter in `examples/panel-extension/`. GitHub's
source ZIP is not an installable package. There is no arbitrary URL
installer or automatic update.

I load catalogue metadata only during discovery. Inspection and
installation each revalidate a fresh index, exact ZIP digest and full
manifest; unsupported APIs/permissions cannot install. Downloads are
bounded, deadline-limited, credential-free HTTPS requests to GitHub's
exact release hosts, with constrained redirects. A withdrawn or changed
selection cannot become a different trusted package. Offline catalogue
failures leave installed addons and ZIP installation available.

## Scope and limits

I keep #801's assets gallery for a separate follow-up. It needs a
bounded, authorized image/history capability rather than access to
internal stores. This PR only establishes external distribution and a
minimal context API.

Windows Tauri may inject native bridge objects into subframes: object
presence is not a grant. My isolated WebView2 fixture proves parent
command execution while direct/raw child calls do not reach it, and
verifies context delivery plus DOM/network restrictions. A trusted
hostile extension can still stall its renderer or attempt
self-navigation; this is not process isolation or a universal offline
guarantee. The installer warns users to trust the author.

I have not run a native Electron/macOS/Linux qualification or changed
the shared daemon. The runtime implementation is shared between desktop
hosts; native GUI validation here is Tauri only.

## Validation

- UI and server typechecks.
- 14 focused server/registry/manifest tests.
- Real right-panel browser tests for ZIP consent, activation scopes,
replacement, revocation, context changes, stale reads and frame
isolation.
- Five catalogue/server checks and seven right-panel browser scenarios
cover metadata-only browse/search, online consent, withdrawal,
incompatibility, offline ZIP fallback, hashes/manifests, redirect
restrictions and size budgets.
- Live public GitHub index and Release download matched SHA-256
`cf80226a40dd077a6a8cc2a4b65bbb46bf33619d62007982bd7dcea34ae4545f`;
installation and activation used a disposable isolated store.
- `node scripts/test-panel-extension-native.mjs` on Windows, using the
application's locked Tauri dependencies and an isolated browser profile:
parent native call allowed; child native calls, parent DOM and network
APIs blocked; session context delivered.
- UI build, Windows Tauri release build (`--no-bundle`) and
packaged-resource smoke passed. Full browser suite results are recorded
in the follow-up validation comment.

Existing oversized files touched only for integration:
`packages/server/src/api-types.ts` (~735 lines),
`packages/server/src/index.ts` (~713),
`packages/server/src/server/http-server.ts` (~2,438).
2026-10-06 23:58:18 +02:00
Pascal André
c954841f09
revert(missions): undo PR #673 without dropping later dev changes
Revert merge c118478805 relative to its first parent after the user confirmed the merge was requested in the wrong conversation. Remove the published Missions plugin, control routes, UI, playbooks and packaging references; do not rewrite dev history or touch durable-refactor work in the separate missions-v2 checkout.

Preserve subsequent PRs #792, #829 and #830. Remove only #829's now-invalid Mission reader import, dismissal hook and dedicated reader scenario, retaining the native questions/permissions dock, file-preview navigation and its other tests. Keep the 2.0.22 package pin and parent-session creation fence.

Validation: revert applied without conflicts; all 139 original merge paths reversed; later-only paths retained except the three minimal interruption-dock integration adaptations. Server and UI TypeScript checks and 18 automation/plugin lifecycle/desktop resource tests pass using existing local dependencies. Staged diff checks are clean. Full hosted CI, browser suites and native 2.0.22 qualification were not rerun for this immediate user-authorized admin revert.
2026-10-02 16:18:06 +02:00
Pascal André
bbbc5e3ff9
merge(dev): resolve mission integration conflicts
Integrate current dev while preserving the mission routes, durable execution controls and specialist cleanup. Retain native usage route dependencies and align local plugin dependencies and architecture guidance with OpenCode 2.0.21.

Combine the workspace file reader with the mission reader in the central session surface. Explicit reader selection dismisses the competing preview without losing stored editor drafts or the composer. Extend the real-session browser regression to cover switching between both readers.

Validate server and UI typechecks and all ten mission browser/i18n tests. Preserve the upstream Tao patch unchanged, including its original whitespace and provenance.
2026-10-01 18:49:47 +02:00
Pascal André
43e3ba72bc
chore(opencode): qualify and recommend stable 2.0.21
Align server/UI client and bundled-plugin pins with OpenCode 2.0.21 while keeping the demonstrated 2.0.7 application minimum and the separate 2.0.20 Codex Usage requirement. The native contract changes only by optional form-cancellation text, so existing callers need no production adapter or proxy expansion.

Extend recommendation/status coverage and update maintained architecture and compatibility references. Scope the lockfile to published OpenCode packages, including optional OpenTUI peer metadata, without refreshing unrelated dependencies.

Qualify minimum/current native suites, generated cancellation with and without a message, selected native Codex credentials, ordinary pruning/UI acceptance, standalone plugin, historical migrations and isolated upgrade retaining daemon version/PID. Typechecks, builds, 71 UI tests and 32 browser cases pass. Preserve the full server suite cleanup EPERM and its successful focused rerun as separate evidence rather than claiming a fix.
2026-10-01 01:43:49 +02:00
Pascal André
e3924113f4
chore(opencode): qualify and recommend stable 2.0.20
Synchronize server/UI client and plugin pins with OpenCode 2.0.20 while retaining the demonstrated 2.0.7 step-timestamp minimum. Review the authenticated runtime contract: existing routes remain stable, while credential operations and optional connection/error metadata are added without requiring production adapters.

Protect the workspace proxy boundary with credential export/create rejection coverage and verify optional provider response bodies survive UI normalization. Update the maintained compatibility and architecture references, including explicit native service startup with the new disabled setting and the unresolved historical Windows import failure.

Validate minimum/current native runtimes, ordinary pruning/UI acceptance, standalone plugin installation, historical migrations and native npm upgrade with daemon version/PID preservation. Clean-install the scoped lockfile and revalidate typechecks, production builds, 745 server tests and 66 UI tests; browser qualification covers 30 scenarios.
2026-09-30 16:11:13 +02:00
Pascal André
824d94dabd
chore(opencode): qualify and recommend stable 2.0.19
Align server/UI client and bundled plugin pins with OpenCode 2.0.19 while retaining the demonstrated 2.0.7 timestamp minimum. The authenticated native schema and published declarations preserve the consumed API contract; runtime improvements concern compaction, context limits, provider cache affinity and Shell conventions.

Extend setup version coverage and update the maintained compatibility and architecture references. Add per-import native/UI fixture diagnostics after late Vite loading stopped on both the new runtime and the 2.0.18 control; do not claim that eager-import diagnostic acceptance fixes that failure.

Qualify both minimum/current native runtimes, historical migrations, isolated npm upgrade without daemon restart, standalone plugin installation, UI/browser behavior and production bundles. Retain the original Windows fixture failures and their bounded diagnostic results in the PR evidence.
2026-09-29 13:44:20 +02:00
Pascal André
b584ffcb3a
merge(dev): reconcile Missions with the current V2 baseline
Integrate dev into the Missions PR without rewriting its reviewed history. Retain mission CRUD routes, authenticated report admission and the 512 KiB bridge limit alongside inspected-window automation and plugin controls. Keep dev's canonical transport, clean builds and 2.0.18 client/plugin dependencies; align the local plugin contract check with the same SDK.

Combine architecture guidance and preserve the technical runtime minimum of 2.0.7. Keep the isolated Missions fixture in the latest-runtime CI lane because its test-only ctx.tool.list driver is absent at that minimum; do not reintroduce legacy compatibility paths.

Validation: server, UI and plugin-contract typechecks; full server/UI/plugin build; 84 backend/compatibility/packaging tests; 59 UI/persistence/browser tests; isolated OpenCode 2.0.18 Missions fixture and workflow YAML parsing. Merge resolutions have no conflict markers or diff-check errors relative to dev.
2026-09-28 10:46:59 +02:00
Pascal André
c76edff89f
feat(missions): add revisable plans and persistent mission control
Make Mission Control actionable with create/edit/delete, persistent disclosures and selection, task dependencies, native attention requests, execution comparisons and a long-content reader above the mounted transcript. Preserve drafts and native per-window layout, and arbitrate browser/reader gestures without losing the preview target.

Add coordinator-only mission.revise with revision CAS, idempotent replay, linked replacements and explicit dependency changes. Retain withdrawn task contracts and late reports, require terminal settlement for outstanding native admissions, and attribute human metadata edits in the bounded history. Mission deletion tombstones the map without deleting conversations.

Cover lifecycle and revision semantics with backend regressions and the isolated OpenCode 2.0.18 fixture, including the repaired authenticated late-report notification. Validate real Solid UI restoration, editor retry identity, native background Forms, transcript/draft preservation, layout cleanup and ten-locale parity. Record Kandev research, scope decisions and separate follow-up opportunities.
2026-09-27 21:36:55 +02:00
Pascal André
4d7408631d
feat(missions): bundle project coordination with the desktop runtime
Provision codenomad.missions through connected-daemon discovery and independent backend presence instead of requiring a source plugin in every project. Bundle the integration for Electron and Tauri, retain project-scoped mission journals across shutdown, and use the same secondary surface as Status.

Complete the existing native actor execution work: persist explicit agent/model/variant choices, validate catalog selections and avoid switching busy actors. Admit assignment and report writes through the authenticated desktop bridge with durable-contract verification, session ownership, connection and worktree fences, and per-send profile environment synchronization.

Advance the older source branch client/plugin pin to 2.0.11 and retain its bounded runtime metadata adaptation. The isolated 2.0.16 mission fixture passes native catalog, selection, busy queues, conflict, environment, reports, reconnect and idempotence checks. Server, UI and plugin typechecks, 31 mission tests, 10 lifecycle/transport tests and six resource-layout tests pass.
2026-09-27 00:57:56 +02:00
Pascal André
f26bed2f8f
chore(opencode): qualify and recommend stable 2.0.18
Align server/UI client and bundled plugin pins with the latest stable runtime while preserving the demonstrated 2.0.7 timestamp minimum. Review the published declarations and authenticated native schema: pairing and Shell signal additions require no CodeNomad API adaptation.

Make the isolated native upgrade fixture follow the recommendation while retaining its 2.0.15 source boundary and live-daemon PID assertion. Extend setup version coverage and update the current compatibility and architecture references.

Validate both minimum/current native runtimes, historical migrations, server/UI tests, setup and tool-image browser scenarios, typechecks and production bundles. Record the intermittent Windows Node pruning/UI heap exit and successful diagnostic rerun without claiming its cause is fixed.
2026-09-26 16:38:40 +02:00
Pascal André
1eb42feaae
chore(opencode): qualify 2.0.16 with aligned clients and plugin (#772)
Align the server/UI client, bundled plugin and recommended runtime at 2.0.16 while retaining the demonstrated 2.0.7 minimum. Verify the real generated-client detailed errors without duplicate proxy prefixes or failed mutation replay; retain explicit installation and daemon restart boundaries.

Independent gatekeeper audited all 63 upstream commits and the full comparison, published packages, native consumers and targeted regressions: zero actionable findings. Native pruning/UI and historical migrations pass across Windows, Linux and macOS; minimum/current runtime contracts and dedicated Tauri jobs pass. Generic browser failures were independently reproduced on the exact 2.0.15 base and documented in the published review. Admin squash merge explicitly requested by the user.
2026-09-24 20:42:08 +02:00
Pascal André
ea3aeb9e3e
fix(opencode): qualify 2.0.15 and preserve generated proxy URLs (#752)
Validation includes isolated minimum/current native suites, historical migrations on Linux/Windows/macOS, targeted unit and browser tests, production builds and two independent gatekeeper reviews with zero actionable findings. Retain startup diagnostics for an unexplained intermittent Windows pruning/UI process exit; the complete instrumented Node 24.20.0 rerun passes locally. Known system-message search fixture timeouts remain documented. Admin merge explicitly requested; runtime activation and desktop installation remain separate actions.
2026-09-23 12:56:03 +02:00
Pascal André
e76e4484ab
fix(opencode): ground compatibility retirement in native contracts
Replace the arbitrary 2.0.11 floor with the 2.0.7 native step-timestamp boundary, keeping 2.0.11 as the separately recommended qualification target. Unlisted, prerelease and custom runtimes negotiate authenticated API requirements; preserve complete CLI labels so unknown builds neither fail solely by name nor enable unintended automatic upgrades.

Finish retiring pre-2.0.4 location/worktree/credential/Form serialization and obsolete catalog events while preserving internal identity, import/cursor authorization, pruning claims and connection fences. Remove the unused global package-manager installer and clean generated server output before packaging so deleted modules cannot survive incremental builds.

Distinguish missing setup, technical incompatibility and optional updates in the shared UI, including minimum rationale and optional restart wording. Retain a usable daemon through older discovery CLIs, check reload capability independently, and suppress installation when bundled npm is unavailable.

Validated 622 server tests, 7 browser scenarios, real reducers and fixture guards, production native suites on 2.0.7 and 2.0.10, and synthetic 2.0.3-to-2.0.7 history migration. Independent gatekeeper re-review closed the production version-parser finding. CI now qualifies the source-defined minimum and latest; rebuilt desktop acceptance is recorded separately.
2026-09-20 23:46:54 +02:00
Pascal André
4a77d162eb
docs(opencode): record final native acceptance and gatekeeper closure
Record completed historical migrations, bounded installation/reload fixtures, canonical WSL provisioning, cross-selection service-action fencing and packaged Electron/Tauri acceptance. Document the native watcher and reload semantics, read-only old-daemon registration fallback, final evidence roots and reproducible isolated commands.

Close local review gates after every actionable finding was corrected and independently re-reviewed. Record the final 621-pass server suite and seven browser regressions while keeping final-head remote CI distinct from earlier green test jobs.
2026-09-20 18:28:15 +02:00
Pascal André
7869e609b3
feat(opencode): enforce stable runtime support and add managed setup
Require stable OpenCode >=2.0.11 <3.0.0 from authenticated daemon metadata before functional clients and plugin provisioning. Keep bounded contract recognition, connection-generation fencing and native ownership checks while retiring old request, response and event conversions.

Unify missing and outdated runtime recovery with Preferences. Install exact stable releases through bundled Node/npm into a verified versioned user prefix, preserve custom and WSL selections, and separate installation from explicit shared-service restart through the native-parent starter. Fence stale setup responses, retain pending folder opening and never replay prompts.

Certify isolated 2.0.3-to-2.0.11 history migration with executable hashes, untouched seed storage, paginated messages, forks, compaction checkpoints, location moves and pending inbox data. Add minimum/latest cross-platform migration and installation gates, packaged npm closure checks, real Solid recovery regressions and supported-contract authority tests.

Validated server/UI types, focused unit and reducer suites, browser captures, actual packaging-prepared Node 24.20.0 installation without system Node, and native location, automation and pruning fixtures. Document packaged-desktop, WSL, broader historical-state and remote-CI evidence as remaining release gates.
2026-09-20 16:09:36 +02:00
Pascal André
ca94224b74
fix(opencode): align current contracts and fence native message settlement (#728)
Align the server/UI OpenCode client and bundled plugin at 2.0.11. Route canonical server.info through authenticated discovery, preserve legacy step timing, and honor hidden Forms protocol fields without rendering their inputs or dropping defaults.

Reconcile terminal tools into the bounded visible transcript and fence native reducer history reads against intervening events. Discard stale pages and coalesce trailing reads so a late settlement snapshot cannot remove a newer streaming assistant. Preserve lifecycle, generation, disposal, location and historical-data safeguards.

Qualify the latest stable runtime in CI and record the resolved version. Each CodeNomad release fixes its minimum supported OpenCode version to the latest stable available when that CodeNomad release ships. Keep one shared compatibility reference and detailed evidence in PR/CI history rather than per-version reports.

Validation includes isolated native runtime/proxy/pruning, automation, environment and fork acceptance; hidden Forms browser coverage; deferred settlement/new-execution regressions; 112 final targeted UI tests, typechecks and production builds. Gatekeeper findings were fixed and independently re-reviewed before merging.
2026-09-20 13:48:53 +02:00
Pascal André
19daab1bbe
merge(dev): resolve Missions against current native runtime
Merge dev bdac05bb into the Missions branch so PR #673 retains its project-local codenomad.missions coordination while adopting the current bundled automation, session-environment admission, native worktree inventory, and desktop lifecycle changes.

Resolve the server import overlap by preserving both Mission route registration and fail-closed session environment errors. Update the architecture guidance to document Missions alongside the current backend-presence automation and pruning boundaries.

Validation: 615 server tests passed with 2 platform skips; 41 focused Missions and session-environment tests passed; server, UI, Electron, and project-local plugin typechecks passed; pruning and automation plugin builds passed; workflow YAML parsed successfully; git diff checks were clean.
2026-09-19 20:10:03 +02:00
Pascal André
bdac05bb25
fix(environment): refresh profile variables before each prompt (#714)
## Summary

Apply the existing CodeNomad profile environment **before every prompt,
custom command and session-shell send**, then forward the request only
after OpenCode acknowledges it. This makes profile changes available to
local commands even when the shared daemon is already running.

No per-conversation settings UI, polling, session-opening writes or
daemon restart. The implementation lives in the shared server for
Electron/Tauri/web parity.

close #711; awaiting reporter validation rather than automatically
closing the issue.

## Implementation

- Build a complete execution-host snapshot; preserve PATH and other
inherited values, normalize Windows key collisions, and exclude internal
authentication/storage variables. WSL reads its selected distro's
environment instead of sending Windows PATH/HOME.
- Apply after ownership checks and worktree mutation admission, using
the pinned native connection. Fail the send on environment errors, stale
connection or disconnect; never log the native environment request body.
- Update help and failure messages in every locale. Changes and removals
take effect on each conversation's next send.

## Validation

- Server suite: **586 passed, 2 platform skips**.
- Focused UI send/SDK/authority tests: **112 passed**.
- Server, UI and Electron typechecks; production UI build passed.
- **OpenCode 2.0.9 / Windows native fixture:** real WorkspaceManager +
guarded proxy + native shells, two conversations, changes/removal, no
mutation on read, Git Bash, unchanged daemon PID. Independent
configuration/database/port; only the fixture service was stopped.
- Reproduction: `node scripts/test-session-environment-native.mjs
<absolute-opencode-executable>`.

## Scope

This changes local session command environments, not provider/MCP/LSP
configuration or daemon-internal temporary files. The native environment
belongs to a session: another client or a new send during existing work
can replace it. Queued prompts do not get individual snapshots, and
native-created subagents are not automatically intercepted. Details:
`dev-docs/SESSION_ENVIRONMENT.md`.

Existing oversized files touched narrowly:
`packages/server/src/server/http-server.ts` (~2,300 lines),
`packages/server/src/workspaces/manager.ts` (~1,200 lines), and
`packages/ui/src/lib/i18n/messages/*/settings.ts` (~600 lines each).
2026-09-19 18:18:56 +02:00
Pascal André
f0a6e2e0dd
feat(browser): add autonomous native previews (#666)
## Summary

- Add autonomous native browser previews for Electron and Windows Tauri,
using the authenticated backend/native bridge already shared with
desktop inspection.
- Ship one bundled **`codenomad.automation`** V2 plugin for browser,
inspect, act and screenshot. All tools are available by backend
presence; ordinary desktop startup prepares the instrumentation. Remove
the Developer Mode toggle, marker-based activation and activation
restart requirement.
- Provision through native discovery using the **connected daemon's
authenticated `config.get` roots**, including WSL and reconnects. Share
installation/lease mechanics with pruning, preserve user-authored
entries and older backends' leases, and unwind stale or interrupted
preparation.
- Preserve session/location ownership, inspected-host pinning, browser
registration/navigation generations, local-window targeting and
authenticated transport. The OpenCode daemon remains externally owned.

## Gatekeeper review — September 18, 2026

Merged current `dev` at `96ef38c6` and repeated independent UI, native
and server reviews after each finding until all three scopes reported
**zero remaining actionable findings**.

Corrections include ordinary-project discovery without Developer Mode;
connected-daemon namespace selection and stale lease cleanup; Tauri
child-webview capability isolation, native-window counting,
primary-renderer reload cleanup, fragment navigation, snapshot lock
ordering and deadline-fenced protocol dispatch; and Electron guest
readiness, redirects and explicit navigation intent. Shared-frame
regressions also cover insecure HTTP and late/rejected native IPC.

### Architecture decision

Current OpenCode V2 documents MCP `_meta.sessionID`; the historical
missing-session-metadata argument no longer applies. A single native V2
plugin remains appropriate for the requested unified automation surface:
it reuses the existing authenticated transport and native controllers,
supports backend presence, and needs no second protocol server or
mode-based permission framework. Plugin loading, tool availability and
execution targeting are separate concerns.

## Validation

- Full server suite: **570 passed, 2 skipped**.
- Electron native suite: **214 passed, 4 skipped**.
- Tauri native suite: **180 passed**.
- Real Solid/browser preview suite: **14 passed**, including real
isolated Electron guests and insecure HTTP.
- Actual isolated OpenCode automation fixtures: **beta-19271, 2.0.3,
2.0.5 and 2.0.7**. Covers mismatched backend/daemon environments, late
discovery, all four tools without activation, execution fences,
independent leases, shutdown and reopening.
- Full isolated native pruning fixture on **2.0.7**, including
concurrency, durable claims, payload isolation, backend presence,
restart and worktree/location integration.
- UI, server and Electron typechecks; Electron production build; Windows
Tauri release build (`--no-bundle`); focused Rust formatting and `git
diff --check`.
- Electron and Tauri packaged-resource smoke checks, plus five desktop
packaging regressions including loading the dependency-free automation
bundle from both layouts.
- CI now runs automation discovery across the runtime matrix, includes
2.0.7, and uses Xvfb for real Electron renderer regressions on Linux.

## Validation boundaries

The fixtures never use or stop the shared daemon or a user's database.
Actual WSL runtime execution and a live packaged WebView2 interaction
smoke were not performed in this pass; WSL resolution and Tauri
controller/capability behavior have focused regressions. The currently
running desktop artifact was not replaced. Agent interruption remains
deadline-based rather than end-to-end native cancellation.
Cross-platform CI must validate the pushed head.
2026-09-18 17:46:14 +02:00
Pascal André
cf537740f1
fix(desktop): preserve shared OpenCode service after CodeNomad exits (#702)
## Summary

Fixes #691.

On Windows, an OpenCode daemon started by the Tauri backend could
inherit its kill-on-close Job Object. Closing CodeNomad would then
terminate the shared daemon even though the server never called `service
stop`.

- Delegate only the official `service start` command through the private
backend-to-native-parent bridge.
- Launch the starter outside backend containment in both Tauri and
Electron, preserving the selected host/WSL CLI, environment, working
directory and Windows wrapper arguments.
- Keep status, password retrieval and authenticated health in the server
lifecycle. Standalone servers continue using the official CLI directly.
- Bound starter deadlines/output, redact failures, and never fall back
into backend containment after a native-start failure.
- Leave daemon descendants externally owned; starter cleanup never
terminates them.

## Validation

- Server and Electron typechecks passed.
- Server suite: 458 passed, two platform skips; final native
bridge/failure tests: two passed.
- Electron native suite: 197 passed, four POSIX skips; final starter
tests: three passed.
- Tauri backend tests: 29 passed; starter tests: two passed, including
verbatim Windows `.cmd` quoting.
- Isolated fake services remain reachable after actual Windows backend
Job Object destruction and Electron process-tree enforcement. The final
lifetime regression also passes after fixture-cleanup hardening.
- Rust module/tests compile-check for macOS ARM64 passed; this is not a
macOS runtime test.
- Tauri Windows release build and packaged-resource smoke checks passed,
with five pre-existing dead-code warnings.

The lifetime tests do not use the real shared OpenCode service or a user
database. The built artifact has not been installed over the other
active development build.

## Maintenance notes

Touched existing oversized source files:
`packages/server/src/workspaces/manager.ts` (~1,040 lines),
`packages/server/src/index.ts` (~680),
`packages/electron-app/electron/main/process-manager.ts` (~640),
`packages/tauri-app/src-tauri/src/cli_manager.rs` (~2,340), and
`packages/tauri-app/src-tauri/src/main.rs` (~2,510). New starter and
regression modules keep the added behavior separately scoped.

## Gatekeeper follow-up

Updated against dev and corrected the findings in `b66cdb50`:
- Fence native side effects before Electron dispatch so a
closing/superseded backend cannot start a service from buffered stdout.
The new regression failed before the fix.
- Reproduce and fix the Linux lifetime fixture's premature cleanup
assertion: wait for Node to reap the child, then require final
containment confirmation.
- Align the native bridge test with the current status contract after
the dev merge.

Post-fix review found no further confirmed issue in the reviewed paths.
Current validation: 533 server tests passed (two skips); 198 Windows
Electron native tests passed (four skips); 30 real Linux
starter/bridge/stop tests passed (three Windows skips); 29 Tauri backend
and two starter tests passed. Server/Electron typechecks passed. CI is
running on the updated head.
2026-09-18 01:23:09 +02:00
Pascal André
83a4124b37
feat(v2): manage session families across worktrees (#649)
## Summary

Manage complete session families across the opened local repository's
native OpenCode worktrees. This supersedes #642 with the V2
implementation and addresses #319.

- OpenCode owns `worktree.refresh/list/create/remove`; CodeNomad
supplies the default directory, named-branch policy, local repository
scope and verified family transactions.
- A project is the opened local checkout and its registered worktrees,
identified through the physical Git common directory. Independent clones
sharing an OpenCode project ID stay separate.
- Session rows, composer selection, search and the Git panel follow
native `SessionInfo.location.directory`. Durable metadata is preserved,
but old `worktreeSlug` metadata does not override native placement.
- Creation uses the selected checkout's revision and the main local
checkout's `.codenomad/worktrees` parent. Uncommitted edits stay in the
source checkout. External registered worktrees remain discoverable.
- Keep copy/open/delete actions in the accessible selector, remove the
redundant action bar, hide root-workspace badges, and explain “Create
and use” in all ten locales.

## Current base and native contract

This branch includes merged `dev@e47e01c6`, including #697's separately
reviewed discovery/status compatibility fix. UI/server pin
`@opencode/client@2.0.4`; the independently managed daemon has no
exact-version startup gate.

- Complete bounded inventories use opaque native cursors; continuations
send only the cursor.
- Moves are serialized across complete families, block active sessions,
verify authoritative native locations and roll back partial failures.
- Native move events refresh placement and the catalogue without
speculative cross-project transfers.
- Opaque worktree identifiers are separate from mutable branch labels.
The main checkout cannot be removed.
- Native detached creation is followed by ordinary Git branch selection,
preserving Git's refusal to reuse a checked-out branch;
branch-attachment failure cleans up through the native API.
- Agent placement instructions distinguish creating a checkout, one-off
commands and moving the conversation's execution context. Explicit
attachment choices are preserved.

## Deletion safety

- Physical checkout reservations cover published and in-flight logical
workspaces.
- Project serialization and deletion fences cover all family
source/destination checkouts.
- Complete inventories and cross-project checks run inside the deletion
fence, including nested workspaces and owned Shell/PTY resources.
- Canonical path, filesystem identity, registration, branch, HEAD and
active sessions are rechecked before removal. Replacement checkouts fail
closed; rollback requires the original identity.

## Recovery and Git-panel corrections

Validation on the real repository exposed serial enumeration of 162
worktrees and concurrent ownership misses launching overlapping
inventories. Git annotations now run with bounded concurrency, pending
catalogue reads are shared, and negative ownership results are
snapshot-cached. A regression exercises 40 concurrent misses.

The Git panel now treats its complete local staged/unstaged inventory as
authoritative instead of resurrecting native-only stale rows. It also
rechecks the selected worktree/request after waiting for native VCS
status.

## Validation

Latest integration validation:

- Isolated official **OpenCode 2.0.0 and 2.0.7**: production
discovery/status, native worktree discovery/create/remove, selected
HEAD/default parent/named branches, independent-clone scope, stable IDs,
nested paths, checked-out/dirty guards, complete family moves and
rollback.
- Real Solid selector browser tests: mouse, keyboard, touch, actions
without session movement, creation source and returned stable ID.
- Targeted server ownership/cache/manager/proxy/route/location tests; UI
session projection/request-authority/lifecycle tests; family
evacuation/rollback and Git model regressions.
- UI/server typechecks and production server build including the UI.
- Installed Windows Tauri application on the actual repository:
dedicated session creation/use, native location and row/composer badges,
Git marker visibility, move back, and agent `session_move` reflected in
both badges.
- Rebuilt UI/server resources installed; full Developer Mode restart
restored the original conversation. Disposable sessions, marker,
worktrees and branches cleaned up. The original conversation remained
attached to `pr649-final`.
- `git diff --check`.

An immediate post-restart desktop run exceeded a 30-second
refresh-button wait; the subsequent completed run passed. These checks
validate the workflows, not a startup latency bound or exhaustive
compatibility of every OpenCode payload.

Earlier branch validation also covered Electron/Tauri native actions,
packaging and desktop parity; those results remain in the PR's
historical validation comments. Detailed contract and latest evidence:
`dev-docs/WORKTREE_SESSION_PLACEMENT.md`.

## Residual limit

OpenCode and Git expose no shared snapshot/CAS transaction. CodeNomad
narrows races with reservations, locks, fences and repeated
authoritative checks; unrelated processes can still mutate state outside
those controls.
2026-09-17 22:57:37 +02:00
Pascal André
e47e01c660
fix(opencode): support server.info discovery without breaking earlier V2 (#697)
## Summary

Follow-up to #695: restore connections to OpenCode 2.0.7, whose
authenticated service metadata endpoint is `/api/info`. Built directly
on current `dev` (`aa51cbb9`).

- Probe `/api/status`, then `/api/health`, then `/api/info`, advancing
**only on HTTP 404**.
- Preserve credentials, the absolute deadline, bounded/validated
responses, and reject redirects.
- Record the discovered route and adapt the pinned client's `GET
server.status()` call to `/api/info`.
- Preserve OpenAPI contract negotiation for unknown releases; no 2.0.7
special case or new minimum version.

**OpenCode 2.0.0 remains supported by the existing health path.** Its
real isolated daemon passes the connection and native location
regression fixture with this change.

This PR contains only the service discovery/transport fix and its
tests/docs. Worktree catalogue performance/cache changes remain in #649
and are not dependencies of this fix.

## Evidence

Comparing all 132 generated-client HTTP method/path pairs against the
installed 2.0.7 OpenAPI schema found 131 unchanged pairs and the
status/info difference fixed here. Experimental routes were already
covered by #695; no additional experimental-route migration is needed.

## Validation

- [x] 44 focused host/WSL lifecycle, shared-service, transport and
negotiation tests
- [x] Server TypeScript check
- [x] Real isolated OpenCode **2.0.0**: production health discovery,
canonical `client.server.status()`, legacy location identity, Forms,
Shell/PTY scoping, SSE, foreign cursor/session refusal and exact move
rollback
- [x] Real isolated OpenCode **2.0.7**: production info discovery,
canonical `client.server.status()`, modern worktree location and
obsolete-selector rejection
- [x] Staged whitespace check

Native fixtures use dedicated temporary databases/configuration and do
not target the shared user daemon. These checks establish discovery and
targeted contract behavior, not an exhaustive certification of all
runtime features.

Signed commit: `16d10fcd`.
2026-09-17 22:18:44 +02:00
Pascal André
fb07e299c7
merge(dev): migrate Missions to the current native plugin contract
Integrate dev aa51cbb9 and retain both Missions and current request-location
CI coverage, panel styling and narrowly scoped plugin documentation. Move the
Missions client and checked plugin dependency to official 2.0.4 packages.

Replace removed plugin.awaitActivation with the native location and plugin
inventories. Validate snapshot project identity against location.get, retain
legacy location context through direct and typed RPC requests, and refresh
tracked Missions when plugin.updated changes capability availability. Keep
Missions optional and its broker restricted to the reviewed snapshot method.

Validation: checked Plugin.define setup against @opencode/plugin 2.0.4;
24 targeted Missions tests passed; server suite 524 passed/2 skipped;
UI stores 444 passed; Electron native 194 passed/4 skipped. Server/UI/Electron
and project-local plugin typechecks pass. Live mission delegation on a native
daemon and non-Windows desktop behavior are not claimed by these tests.
2026-09-17 00:41:01 +02:00
Pascal André
aa51cbb9ff
fix(opencode): support earlier and modern V2 runtime contracts (#695)
Closes #694

## Status

This PR contains the stable 2.0.4 client migration, authenticated V2
discovery fallback and shared cross-runtime compatibility
implementation. The validation results below are local execution
evidence; the newly added CI matrix must also run on the updated PR
head.

## Published changes

- Pin UI/server to official `@opencode/client@2.0.4` and the bundled
pruning plugin to `@opencode/plugin@2.0.4`, with the corresponding
lockfile migration.
- Probe authenticated `/api/status` first. Fall back to earlier V2
`/api/health` **only on HTTP 404**, retaining the endpoint, credentials,
64 KiB response bound and absolute deadline. Authentication, transport,
server and malformed-status failures do not trigger fallback.
- Migrate the canonical UI/server contract to stable Forms, permission
decisions, command names, interrupts, renaming, forks, message lookup
and inbox delivery.
- Update the guarded proxy for stable methods/paths, native
active-session envelopes, native `cursor.next`, global Form directory
headers and current resource/plugin events.
- Follow the renamed upstream platform binary `bin/opencode(.exe)` while
retaining public CLI aliases.

Discovery success alone does not establish compatibility with an earlier
runtime's conversation APIs.

## Shared cross-runtime compatibility

### Shared cross-runtime integration

- Introduce one connection-scoped adapter under
`packages/server/src/opencode/compatibility/`, used by both the guarded
UI proxy and direct server callers, including Yolo.
- Select the contract using authenticated daemon identity rather than
the CLI executable's version. Recognize unknown publications through
authenticated, bounded OpenAPI negotiation instead of an exact
runtime-version gate.
- Bind authorization, client, transport and profile to one connection
generation. Fence actual mutation dispatch after asynchronous body
preparation; prevent late old streams from publishing or invalidating
their replacement.
- Translate earlier V2 routes, methods and payloads before sending.
Never retry a mutation speculatively after 400, 404, authentication or
transport failures.
- Normalize legacy HTTP inbox timestamps, including compaction
admission, before the pinned Solid reducer. Preserve the distinct native
SSE inbox schema and durable event metadata.
- Restore legacy catalog/content invalidation and permission-event
convergence. Preserve meaningful method/path/status/profile diagnostics
and native 401 error envelopes.

### Location authority and pruning

- Preserve legacy workspace identity internally through request
serialization, catalog/cache keys, Forms, imports, cursors, events and
exact move rollback. Modern public APIs continue to reject obsolete
workspace selectors.
- Authorize complete locations before translating directories or
synthesizing upstream Form/location headers. Keep native cursor tokens
unchanged.
- Preserve session/path ownership, worktree mutation fences and the
restricted proxy allowlist. Generic RPC, plugin management and
destructive revert commit remain blocked.
- Carry verified legacy identity through pruning membership checks,
preview and the existing transactional storage/claim fence; do not relax
deletion checks.
- Treat native idle records as control records, retaining
identity/outcome without fabricated assistant text.
- Add a common multi-runtime CI matrix and the audit/evidence register
in `dev-docs/OPENCODE_V2_COMPATIBILITY.md`, including preserved
historical architecture findings.

## Local validation

All native tests use isolated synthetic daemons, configuration and
databases, never the shared daemon or user storage.

| Check | Result |
| --- | --- |
| Same native suite on beta-19271, 2.0.3, 2.0.4 and 2.0.5 with pinned
client/plugin 2.0.4 | Passed |
| Full native pruning suite with non-null legacy workspace identity on
beta-19271 and 2.0.3 | Passed |
| Rendered UI on modern 2.0.5 and beta-19271 with non-null identity |
Passed |
| Native 2.0.5 suite inside Ubuntu/WSL, using a temporary Linux
filesystem root | Passed |
| Server tests (after CI follow-up) | 500 passed, 2 skipped |
| UI store tests | 441 passed |
| Electron native tests | 194 passed, 4 skipped |
| UI/server/Electron typechecks | Passed |
| UI/server and Electron production builds | Passed |
| Windows Tauri release executable (`--no-bundle`) | Passed |
| Electron/Tauri packaged-resource smoke checks | Passed |
| Desktop packaging invariants | 4 passed |
| `git diff --check` | Passed |

The native matrix exercises the actual shared connection, generated
client, UI fetch adapter and guarded proxy: voice-off/on instructions,
prompts, commands, session Shell, background Shell output
cursors/removal, UI/Yolo permission acceptance and rejection, busy inbox
delivery/cancellation, interrupt/wait, staged revert clearing,
session/global/worktree Forms, native pagination, export/import,
same-directory legacy identities, scoped events, foreign authority
refusal and exact rollback.

Pruning coverage includes actual storage identity and execution claims,
busy refusal, idempotency, competing admission, next-model payload, fork
isolation, pre-compaction history, restart, backend presence
disposal/reopening and crash expiry. Rendered tests cover
individual/group/response/session cleanup, confirmation counts,
persistence and reload.

## Autonomous gatekeeper review

Review/fix/retest loops concluded with no remaining actionable findings
in the reviewed transport/lifecycle/proxy,
identity/UI/events/pruning/rollback, scripts/CI and rendered-fixture
scopes.

Corrections included dispatch-generation races, compaction admission
normalization, independent negotiation cancellation, first-request
profile resolution, preservation of streamed 401 envelopes and direct
Shell/PTY identity propagation. A final rendered failure was traced to
the fixture awaiting only the first RPC of a cross-message deletion; the
fixture and regression now await every affected message. Production
deletion guards were not relaxed.

This scoped approval does not close the separately preserved historical
architecture findings.

## Validation boundaries

### CI follow-up

The first run on `a0c1c943`
([35142559235](https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/35142559235))
failed in five native jobs. The local bundle tests had missed the
standalone npm archive's missing sibling import, and Windows legacy jobs
exposed a short-path versus canonical-path project comparison. The
dependent artifact-comment job reported that same build failure.

The follow-up makes the plugin's identity primitives self-contained,
adds an isolated npm-file-list regression for all exported entrypoints,
and corrects project identity selection with alias and nested-repository
regressions. The packed-plugin failure was reproduced before correction;
the corrected installed package passes the native suite on Windows 2.0.4
and Linux/WSL 2.0.4/2.0.5. The corrected 2.0.4 shipped bundle also
passes rendered/native pruning. Fresh full beta-19271 and 2.0.3
`--legacy-pruning` suites pass under a reproducing Windows short-path
TEMP root after the final exact-directory fix. Independent final reviews
approve packaging/documentation and worktree identity with zero
actionable findings. Remote checks on the new head remain the CI
authority; these are local follow-up results.

- Full interaction with the installed desktop application through
Developer Mode was not verified because Developer Mode is inactive. The
release executable and packaged-resource checks above are build/smoke
evidence, not a fresh-install interactive certification.
- Remote run
[35146390688](https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/35146390688)
at `486e856d` passes all runtime-contract jobs, Windows/Linux native
pruning, service compatibility, general tests and both Tauri checks.
macOS passes packed-plugin loading/pruning, then times out on late
bundle discovery. The fixture now canonicalizes its root before daemon
startup to align `/var` configuration paths with `/private/var` FSEvents
paths. Late installation remains mandatory and asserts the plugin was
initially absent; failures now upload isolated diagnostic
logs/inventory/captures. Actual macOS confirmation remains pending on
the new head.
- Interactive native TUI validation was not performed.
- The passing WSL result is Linux-native execution inside WSL, not a
complete Windows-host/WSL desktop traversal. With the fixture's
configuration directory under `/mnt/c`, conversation/proxy checks passed
but late plugin discovery timed out; the otherwise equivalent
Linux-filesystem run passed. Mounted-configuration late discovery
remains a documented environment limitation.
- Other audited publications are declaration-reviewed, not individually
native-certified. The four-runtime matrix does not imply universal V2
compatibility.
- Earlier PR artifacts do not contain the completed cross-runtime
implementation; use artifacts built from the updated head. Local passing
results do not imply that the updated remote CI has completed.
2026-09-16 23:48:08 +02:00
Pascal André
dec86c5d96
feat(missions): enforce native playbook contracts
Require structured Pocock evidence for completed diagnosis, implementation, independent standards/spec reviews, resolution, and final validation. Fence fresh reviewer and validator roots while requiring review resolution to reuse the implementer, and cover a full dynamic Pocock cycle plus a breadth-first Wayfinder map.

Align the project-local plugin with beta-18999 runtime behavior by emitting native typed system parts, JSON-normalizing optional RPC output, using the typed RPC call as the capability probe, and keeping the runtime entry free of an SDK import. Add a dedicated compile-time plugin contract check to CI.

Record the private-runtime spike proving busy-root queue delivery, resume, correlated synthetic reporting, restart restoration, idempotent admissions, typed invalidations, owned broker access, and shared project storage across worktrees. Targeted mission, plugin, and broker tests pass alongside server and plugin typechecks.
2026-09-04 13:01:02 +02:00
Pascal André
366d934e3a
feat(missions): add native session mesh control plane
Introduce the project-local codenomad.missions V2 plugin with a durable append-only mission map, deterministic OpenCode inbox admissions, and the compact mission.inspect, mission.delegate, and mission.report tool interface. Root sessions remain visible native actors, only coordinators may change topology, and blocked tasks are mapped without a hidden workflow interpreter.

Add constrained CodeNomad snapshot brokering instead of exposing generic plugin RPC. Workspace-owned locations, active plugin inventory, project identity, root-session membership, actor limits, and foreign-session fences are checked at their respective seams. Queue/resume delivery supports busy actors and deterministic IDs make interrupted dispatch and report notification retries idempotent across restarts.

Ship dynamic custom, Pocock bug-expedition, and Wayfinder playbooks plus provenance and architecture documentation. Pin the local plugin contract to beta-18999 alongside the reviewed V2 client.

Validated with the server TypeScript check and 12 focused mission, plugin registration, recovery, authorization, idempotence, and broker tests.
2026-09-04 13:01:00 +02:00
Pascal André
52f0e62935
feat(opencode): migrate CodeNomad to native V2 (#647)
# OpenCode V2 Migration and Desktop Restructuring

## Summary

This branch replaces CodeNomad's OpenCode V1 SDK, custom plugin, and
per-workspace runtime architecture with the native OpenCode V2 client
and one shared OpenCode service. It intentionally ships no V1 runtime
fallback.

The scope includes native location ownership, Forms and Shell resources,
project-wide cursor pagination, bounded resident message windows,
streaming reconciliation, duplicate-folder instances, multi-window
Electron/Tauri behavior, content-addressed restore, worktree mutation
fencing, and reproducible desktop packaging.

The reviewed implementation head is `dea20996`. UI and server resolve
`@opencode-ai/client@beta` to `beta-18999`; the runtime CLI is managed
independently. The official comparison baseline remains `upstream/beta`
at `eb1ac54d73`.

Full details: [`MIGRATION_V2.md`](./MIGRATION_V2.md),
[`DESKTOP_V2_COMPARISON.md`](./DESKTOP_V2_COMPARISON.md), and
[`dev-docs/DEV_V2_CODE_REDUCTION.md`](./dev-docs/DEV_V2_CODE_REDUCTION.md).

## Stabilization Fixes

- Preserve manual scroll escape across pagination, remote updates,
capped-window shifts, tab changes, and desktop restore; only a local
prompt submission forces the bottom.
- Keep the timeline resident window capped at 200 messages. Ordered
window slides use Virtua shift, while reorder, pagination, and
non-aligned replacement remount with a fresh measurement epoch.
- Finalize the temporary `200 -> 201 -> 200` transition in a
generation-guarded microtask before paint and compensate followed
content growth from a `ResizeObserver` attached to the mounted Virtua
root.
- Keep all pending inbox prompts after delivered transcript messages in
native inbox admission order.
- Add a persisted `steer`/`queue` follow-up preference; the alternate
shortcut uses the inverse while idle sends remain `steer`.
- Edit a pending prompt only after authoritative inbox cancellation
succeeds, then restore its text, structured payload, files, and agent
references to the composer.
- Expose native `session.background` through the ownership-checked
proxy, a prompt action, and `Ctrl/Cmd+B`.
- Remove the duplicate model-selector "show all" action and make the
favorites toggle label reflect its current action.
- Restore permission/Form synchronization plus skill, reference,
MCP-resource, and websearch-provider reads in the reviewed proxy
allowlist.
- Validate and preserve native location workspace identities for
root/worktree requests, session moves/imports, cursors, and global
Forms.

## Capability Decisions

| Capability | Native V2 availability | CodeNomad `DEV-v2` decision |
Revisit or complete when |
| --- | --- | --- | --- |
| Assistant content mutation | `session.messageUpdate` authoritatively
replaces the text, reasoning, and tool content of a completed assistant
message. There is still no generic deletion of a complete delivered
message or mutation of user messages. | Restores individual
tool/reasoning deletion through the native update route. Arbitrary
whole-message deletion remains absent rather than locally simulated. |
Add broader deletion only if V2 exposes an authoritative operation for
the required message type. |
| Bulk tool and reasoning cleanup | No native bulk operation, but
`session.messageUpdate` can safely compose cleanup one assistant message
at a time. | Restores selected-group and full-session technical-part
cleanup through authoritative per-message updates. | A native bulk
operation is only needed if per-message updates become a measured
performance problem. |
| Delete-to-boundary / undo | `session.revert.stage` and
`session.revert.clear` use V2 staged-revert semantics instead of
arbitrary deletion. | Command-palette Undo uses `revert.stage`; Redo
clears the staged revert through `revert.clear`. | Revisit only if V2
adds broader authoritative transcript deletion. |
| Compaction | Native checkpoint compaction summarizes the older head
and retains a server-selected recent tail controlled by
`compaction.keep.tokens`. | Uses `session.compact`; there is no
message-level selective compaction. CodeNomad displays the partial
summary through the same reactive streaming path as text and reasoning,
then replaces it with the terminal summary. | Add scoped controls only
if V2 defines scoped compaction semantics. |
| Transcript pagination authority | Message pages expose only opaque
previous/next cursors. They provide no transcript revision, total,
absolute range, position, or random seek target. | Uses bounded cursor
traversal and a 200-message resident window, with defensive
reconciliation after destructive events. It cannot prove that pages
loaded across compaction or revert belong to one snapshot. | V2 adds
snapshot revision authority
([#43766](https://github.com/anomalyco/opencode/issues/43766)) and
navigable position metadata
([#44660](https://github.com/anomalyco/opencode/issues/44660)). |
| Full-session search | `session.list(search)` filters session titles
only. There is no server transcript-search endpoint returning message
identity, excerpt, navigable position, and transcript revision. |
Retained through exhaustive message-cursor traversal while keeping only
the 200-message resident window and collected matches. Large searches
still fetch every page. | V2 exposes transcript search
([#45801](https://github.com/anomalyco/opencode/issues/45801)) with a
target that composes with transcript revision and seek metadata. |
| Queued prompt management | Native inbox list, cancel, steer, and queue
operations cover authoritative follow-up delivery. The TUI's edit action
cancels a queued prompt before restoring it to the composer; it is not
an atomic inbox update. | Implements persisted primary `steer`/`queue`
delivery, inverse alternate shortcut, delivery switching, cancellation,
cancel-first composer restoration with structured payload retention, and
native-order timeline projection. | Revisit atomic editing or reordering
only if V2 adds an authoritative operation and a concrete product need
is established. |
| Background execution | Native `session.background` moves blocking
tools out of the foreground; Shell resources separately support listing,
bounded output, and removal. | Exposes `session.background` through the
proxy and UI with `Ctrl/Cmd+B`, and replaces the custom process manager
with native Shells. Unsupported controls such as rename remain removed.
| Add controls only when the native session or Shell APIs support them.
|
| Service lifecycle | V2 uses one shared externally owned service rather
than one runtime per workspace. | CodeNomad discovers or starts the
service. Stop Workspace evicts the native location after its final
logical owner is removed; application shutdown never stops the daemon. |
No parity work planned unless V2 changes service ownership semantics. |

## Validation

Migration gate (`878550ca`):

- UI and server TypeScript typechecks.
- 86 focused UI tests covering follow, pagination, queue ordering,
bottom pinning, shortcuts, delivery resolution, and model visibility.
- 64 focused server tests covering proxy ownership/location enforcement
and shared-service workspace identity.
- Production UI/server build.
- Tauri `cargo check` and all 121 Rust tests.
- `git diff --check`.

Timeline stabilization (`dea20996`):

- UI TypeScript typecheck and all 68 focused timeline, pagination,
request-authority, and restore tests.
- Tauri release build against the `beta-18999` lock.
- Native Developer Mode validation: no remount or empty frame during
capped shifts, same-cycle growth compensation, preserved manual escape,
and inactive-tab anchor restore within 0.3125 px.

## Remaining Gaps

- Active-session-location authority for MCP/plugin status.
- Optional interactive PTY, session export, and reference/MCP-resource
attachment UI.
- Desktop release signing/notarization hardening.
2026-09-04 11:36:11 +01:00
heunghingwan
ca06bd99d7
feat(yolo): move permission auto-accept (Yolo) to the server (#561)
## Summary

Yolo (permission auto-accept) currently lives entirely in the UI. Each
browser keeps its own toggle in `localStorage` and auto-replies to
permission requests over a 4-hop path (`OpenCode → server SSE → UI →
server proxy → OpenCode`). The server — which already sits on the event
stream that carries every `permission.asked` — does none of the work.

This PR makes the **server authoritative**: it owns the toggle state,
resolves family-root inheritance, and performs the auto-reply in-process
via loopback using the same `"once"` semantics the UI used to send. The
UI becomes a pure view: it toggles via REST and mirrors state from a
`yolo.stateChanged` SSE event.

## Why

- **Correctness**: the server already consumes the instance SSE stream
(`InstanceEventBridge`); auto-accepting there is the natural choke point
instead of bouncing to the UI and back.
- **Multi-client**: previously each browser had independent
`localStorage` state and never synced. Toggles now broadcast to all
connected clients in real time.
- **Headless**: Yolo keeps auto-accepting even when no UI is connected
(useful for long autonomous runs).
- **Latency**: drops from 4 hops to a single in-process loopback call.

## What changed

**Server (new, authoritative)**
- `permissions/auto-accept-store.ts` — in-memory state keyed by family
root. Faithful port of `resolvePermissionAutoAcceptFamilyRoot`:
fork/`revert` sessions root at themselves; enabling any member enables
the whole family.
- `permissions/auto-accept-manager.ts` — subscribes to `instance.event`,
builds the session tree from `session.created/updated/deleted`
(`properties.info`), intercepts `permission.v2.asked` /
`permission.asked`, dedupes in-flight replies, emits `yolo.stateChanged`
/ `yolo.autoAccepted`, clears per-instance state on
`workspace.stopped/error`.
- `permissions/opencode-replier.ts` — default replier calling OpenCode
directly (`getInstancePort` + auth header), mirroring
`background-processes/manager.ts`.
- `server/routes/yolo.ts` — `GET/POST
/workspaces/:id/yolo/sessions/:sid[/toggle]`, following existing route
conventions.
- `api-types.ts` / `events/bus.ts` — `YoloStateResponse` + the two new
event types registered in `onEvent` so they flow over `/api/events`.

**UI (pure view)**
- `permission-auto-accept.ts` — removed `localStorage`, persistence, and
drain logic; now a runtime (non-persisted) projection.
`resolvePermissionAutoAcceptFamilyRoot` is **retained as a display aid**
so the badge still lights up for child/sub-sessions of an enabled family
(preserving the inheritance UX).
- `instances.ts` — toggle calls REST (optimistic, reconciled on success,
reverted on failure); subscribes to `yolo.stateChanged`;
`ensureYoloStateSynced` backfills the active session's state on first
connect (deduped per session, reset on SSE reconnect so it re-syncs
after a server restart).
- `session-events.ts` — removed the three
`drainAutoAcceptPermissionsForInstance` hooks (the server drains now).
- `api-client.ts` — `getYoloState` / `toggleYolo`.

## Behavior parity

| Aspect | Before | After |
|---|---|---|
| Reply semantics | `"once"` | `"once"` (unchanged) |
| Inheritance | whole family (root + non-fork descendants) | **same** |
| Fork isolation | `revert` session is its own root | **same** |
| Persistence | UI `localStorage` | none (intentional, see Notes) |
| Multi-client sync | ❌ independent per browser | ✅ real-time via SSE |
| Works with UI closed | ❌ | ✅ |
| Auto-reply path | 4 hops | 1 in-process hop |

## Testing

32 unit tests added (`node:test`), all passing. Server + UI typecheck
clean.

- **Store (16)**: inheritance (parent/child/sibling), fork isolation,
cyclic parent chains, late parent discovery, `revert` re-rooting,
per-instance independence, tree maintenance.
- **Manager (13)**: real `properties.info` event shapes, v2 vs legacy
reply, in-flight dedup + retry-after-resolve, `yolo.stateChanged`
emission, `session.deleted` keeps toggle, `workspace.stopped` clears
state, `stop()` unsubscribes.
- **UI (3)**: retained `resolvePermissionAutoAcceptFamilyRoot`
display-projection tests.

## Notes for reviewers

- **No persistence is intentional** for this milestone — server restart
resets all Yolo state (matches the agreed scope). The UI mirror
self-heals via SSE reconnect + `ensureYoloStateSynced`. Persistence can
be layered on later (e.g. into `~/.config/codenomad/config.json`)
without touching the manager.
- **Family-root resolution lives in two places on purpose**: the server
resolves it to decide whether to auto-reply; the UI resolves the same
pure function to render the badge instantly (synchronous memo). Both are
faithful ports; no network round-trip is added for display.
- **`properties.info` nesting**: OpenCode wraps session records under
`properties.info` for `session.*` events (permission events are flat).
The manager handles both and the tests use the real nested shape to
guard against regressions.
- `getYoloState` / `yolo.autoAccepted` are wired but `yolo.autoAccepted`
is not yet consumed by the UI — it's available on the wire for future
observability (e.g. an audit log / toast).

## Risk / rollback

The change is additive on the server and the UI gracefully degrades to
the SSE mirror. If the server lacks the new routes (mixed-version), the
UI's optimistic toggle still flips locally and `toggleYolo` failures are
logged + reverted, so no hard breakage.

---------

Co-authored-by: Pascal André <pascalandr@gmail.com>
2026-07-08 21:13:44 +02:00
Pascal André
d9ff1e03cf
revert: remove tracked NomadWorks task artifacts (#531)
## Summary

This PR removes tracked NomadWorks/OpenCode/task-tracking artifacts that
were accidentally committed to the repository and now interfere with
local NomadWorks usage for other contributors.

NomadWorks generates local workflow state such as `tasks/`, SCR
registries, and codemaps as needed. Keeping those generated artifacts
tracked in the shared repository creates stale state, false active
tasks, and confusing/broken NomadWorks behavior for other users who
initialize or run the workflow locally.

## Problematic commits / artifacts

The cleanup addresses artifacts introduced by the following
Shantur-authored commits:

- `e708c565ef`
  - `docs(wake-lock): record wake-lock change workflow`
  - Introduced NomadWorks wake-lock SCR/task artifacts:
    - `docs/scrs/SCR-2026-04-21-001-wake-lock-system-sleep-only.md`
    - `tasks/current.md`
-
`tasks/discussions/DISCUSSION-001-wake-lock-behavior-change-for-macos-sleep-vs-screen-lock.md`
    - `tasks/todo/055-wake-lock-investigation.md`
    - `tasks/todo/056-wake-lock-behavior-change.md`
    - `tasks/todo/057-implement-system-sleep-only-wake-lock.md`

- `a337c19b63`
  - `Init nomadworks`
  - Introduced tracked NomadWorks/OpenCode initialization artifacts:
    - `.nomadworks/**`
    - root `codemap.yml`
    - `docs/scrs/current.md`
    - `docs/scrs/done.md`
    - `tasks/done.md`
    - `.opencode/opencode.jsonc`
    - `.opencode/package-lock.json`
- The removed `.opencode/opencode.jsonc` only contained the OpenCode
schema plus a commented NomadWorks plugin entry:
    - `"$schema": "https://opencode.ai/config.json"`
    - `// "@neuralnomads/nomadworks@0.1.0-rc.10"`
- The removed `.opencode/package-lock.json` was an orphan lockfile with
no tracked `.opencode/package.json`. It pinned old OpenCode
plugin/runtime packages, notably:
    - `@opencode-ai/plugin@1.14.24`
    - `@opencode-ai/sdk@1.14.24`
- Those pinned OpenCode versions are stale for the current
CodeNomad/OpenCode integration, so keeping this lockfile is misleading
and can imply an unsupported local plugin setup.

- `b06b8104a5`
  - `Add task specifications for Phase 5 advanced input features`
  - Introduced tracked task-planning artifacts including:
    - `tasks/todo/015-keyboard-shortcuts.md`
    - `tasks/todo/020-command-palette.md`
    - `tasks/todo/021-file-attachments.md`
    - `tasks/todo/022-long-paste-handling.md`
    - `tasks/todo/023-symbol-attachments.md`
    - `tasks/todo/024-agent-attachments.md`
    - `tasks/todo/025-image-clipboard-support.md`

- `0a3ac6cbf2`
  - `docs: add theme overhaul task series`
- Introduced tracked task-planning artifacts `tasks/todo/041-*` through
`tasks/todo/045-*`.

- `cb56eed4f9`
  - `docs: add tailwind refactor follow-up tasks`
- Introduced tracked task-planning artifacts `tasks/todo/046-*` through
`tasks/todo/048-*`.

- `7267baf23d`
  - `docs: queue remaining style cleanup tasks`
- Introduced tracked task-planning artifacts `tasks/todo/049-*` through
`tasks/todo/054-*`.

Some older mixed product commits also added `tasks/done/*` or
`PROGRESS.md` while introducing real product code. Those commits should
not be fully reverted because that would remove product functionality,
so this PR removes the stale tracking files directly in a final cleanup
commit.

## Changes

- Reverts the previous partial NomadWorks cleanup so the full original
faulty state can be reverted cleanly.
- Fully reverts the original `Init nomadworks` commit.
- Fully reverts the wake-lock NomadWorks workflow artifact commit.
- Removes remaining stale historical progress/task-tracking files that
are not used by CodeNomad runtime, builds, or GitHub workflows.
- Removes stale `.opencode` root config/lock artifacts introduced by the
NomadWorks initialization because the config only references a commented
NomadWorks plugin and the lockfile pins unsupported old OpenCode
packages without a matching package manifest.

## Validation

- `git status --short` is clean.
- `git diff --check upstream/dev..HEAD` passes with no output.
- Final diff only removes tracked NomadWorks/OpenCode/task/progress
artifacts; no runtime source files are changed.
2026-06-08 17:20:31 +01:00
VooDisss
b142c2a681
feat: add CodeNomad architecture guide skill for contributors (#493)
## Summary

Add comprehensive architecture and SDK navigation skill to help
contributors navigate the codebase without missing related code.

## What's Included

- **Architecture Overview** — 6 functional areas (UserInterface,
ServerBackend, DesktopClient, SpeechAndAudio, BuildAndPackaging,
CloudflareDeployment), package map, and key entry points
- **UI Conventions** — SolidJS patterns, i18n system (7 locales),
signal-based stores, component styling
- **Server Conventions** — Fastify route patterns, API types,
configuration, testing approach
- **Desktop Conventions** — Electron + Tauri parity rules, native API
abstractions
- **SDK API Reference** — All 10 OpenCode SDK V2 categories used by
CodeNomad with signatures and wrapper locations
- **SDK Critical Behaviors** — Schema gotchas (assistant part metadata,
ignored flag asymmetry), decision matrix, race conditions
- **SDK Integration Patterns** — Client lifecycle, worktree routing,
error handling, optimistic updates
- **Feature Traces** — 5 end-to-end flows with decision branches:
permissions, sessions, speech, background processes, git clone
- **Anti-Patterns** — Common mistakes with file references to correct
implementations
- **Implementation Checklist** — Actionable steps before submitting
changes

## Design Decisions

- All navigation guidance uses standard grep/file search tools (no RPG
MCP server required)
- Split into 8 reference files to stay under 300 lines each
- File references included for every anti-pattern and convention
- Decision branches in feature traces (not just linear flows)
- Weighted quick start by contribution frequency

## Files Added

`
.opencode/skills/codenomad-architecture-guide/
├── SKILL.md
└── references/
    ├── architecture-overview.md
    ├── ui-conventions.md
    ├── server-conventions.md
    ├── desktop-conventions.md
    ├── sdk-api-reference.md
    ├── sdk-critical-behaviors.md
    ├── sdk-integration-patterns.md
    └── feature-traces.md
`
2026-05-21 17:15:49 +02:00
Shantur Rathore
0eca320d5e Use never release notes format 2026-05-14 10:38:03 +01:00