Skip to content

R1: chat actions on the durable engine - #81

Merged
siddWednesday merged 76 commits into
mainfrom
feat/computer-use
Aug 14, 2026
Merged

R1: chat actions on the durable engine#81
siddWednesday merged 76 commits into
mainfrom
feat/computer-use

Conversation

@siddWednesday

@siddWednesday siddWednesday commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

R1 - chat actions on the durable engine

The first release of the assistant's act pillar, built to docs/COMPUTER_USE_PLAN.md (R1) and docs/R1_CHECKLIST.md (17/19 boxes; the last two are this PR + the release dispatch). The model only proposes a structured Action; a durable on-device pipeline guarantees it - exactly once, gated, verified. Design: docs/ASSISTANT_ARCHITECTURE.md. Port decisions: docs/PORTING_MAP.md.

What ships

macOS (the headline): ask in chat and it acts - reminders, calendar events, iMessage, mail, open URLs/app schemes, plus instant reads (calendar/reminders/contacts). Every mutation flows propose -> durable queue -> gate -> semantic rail -> verify. Calendar and reminders are read-back verified ("done" means we checked the app and it is there); sends are single-attempt behind the gate (a wrong verify can never double-send).

Windows: the full app builds and packages green (run 31779453356, 414MB NSIS artifact). The Windows semantic rail (local Outlook COM - writes land locally and sync, matching the mac approach; Graph as a boundary-tested online fallback port) ships behind the DeviceController port; chat exposure on Windows is a recorded fast-follow, as is the signing cert.

The engine (@offgrid/use, in shared - companion PR): the durable queue (leases, visibility timeout, idempotency dedup, scheduling), the Action lifecycle state machine with crash-resume snapshots, the retry policy (retry-once-with-verify; irreversible/fuzzy never re-fire), the payload-hash-bound gate (approve = byte-for-byte what runs; edits re-bind and re-gate), the DeviceController port + handler registry (fail-closed registration), and the write-ahead effect journal that makes kill-mid-execute safe.

The guarantees, each a passing test

Guarantee Proven by
Exactly-once (dedup at enqueue, lease exclusivity, crash-resume without re-fire) engine suite + use-storage.integration.dbtest on the real app DB
Approve runs exactly the approved payload (hash equality end to end) gate-host.integration.dbtest
An irreversible send fires once, ever - even on timeout after the send went out engine suite ("the reviewer scenario")
False-ok writes are caught by read-back and retried exactly once verification.integration.dbtest on the shipped registry
Pro behaviour byte-for-byte unchanged (legacy approval queue path) extension suite + the hook-active routing tests
The golden path in the real app e2e/app250-chat-action-engine.spec.ts (5s, production code with fakes only at the model + OS boundaries)

Evidence

  • Golden path screenshot (validated): e2e/screenshots/r1-chat-action-verified.png - the ask, the tool activity row reminders_create -> Created the reminder., and the confirmation, on a fresh free profile.
  • Manual smoke on a real Mac: reminder created in Reminders.app via chat (and the before-shot of the model hallucinating "I've set a reminder" with tools off - the failure mode this PR closes).
  • Video: not captured - this branch was built in a headless sandbox; APP-250 exercises the interaction path and its screenshot is validated. A short clip of the golden path can be added at review if wanted.

Test output

@offgrid/use (shared):        89 passed (89)
core fast suite:              279 files passed | 3 skipped - 2220 tests
pro suite (desktop-pro):      193 files passed - 2235 tests
db journeys:                  green (incl. 4 new engine dbtest files)
e2e APP-250:                  1 passed (5.0s)
pre-push coverage gate:       all four new-code floors met
  statements 99.5% | branches 60.3% (floor 57) | functions 60.0% (floor 52) | lines 99.5%
Windows CI:                   success (installer artifact, 414MB)

Also in this branch

  • Suite health: core-only checkouts no longer collect orphan pro tests; ELECTRON_RUN_AS_NODE stripped at both Electron-spawn seams; a startModelServer await race fixed; pro test files untracked from core (they live in desktop-pro, verified identical).
  • CI: windows-build.yml gained the shared checkout; both workflows now build @offgrid/use alongside sync (the release would have failed without this).
  • Product decisions recorded in-plan: actions are Tools (category seam, fail-closed), Tools default stays off until R2's per-turn router (a default flip reroutes every chat turn - measured as 20 behaviour-test failures), discoverability hint is checklist item 18b.

Release items (recorded, not gates)

Windows signing cert - Windows chat-tool exposure - Outlook read-back verbs - Graph sign-in - the composer hint - pro approval-executor migration to resolveActionGate (desktop-pro, unblocked now).

🤖 Generated with Claude Code

Greptile Summary

The PR adds macOS chat actions backed by a durable action engine, native semantic rails, approval gating, and read-back verification, while adding Windows rail and build infrastructure.

  • Registers native calendar, reminder, contacts, messaging, mail, and URL tools on macOS.
  • Persists mutations through the shared action queue and worker, with approval and verification adapters.
  • Adds EventKit, Contacts, and AppleScript helper commands plus Outlook PowerShell support.
  • Updates packaging and CI to build the helper and shared action-engine dependency.

Confidence Score: 3/5

The PR should not merge until semantic verification checks requested fields and the native helper is built for every supported macOS architecture.

Title-only read-back can report incorrectly created actions as complete, and the fixed-arm64 helper cannot execute inside the x64 local application path; the mutable workflow dependency is an additional non-blocking supply-chain concern.

Files Needing Attention: src/main/actions/verification.ts, scripts/actions-helper/main.swift, scripts/build-actions-helper.sh, .github/workflows/windows-build.yml

Security Review

The new Windows shared-checkout steps use mutable action tags while receiving a cross-repository token. Pin them to the same immutable checkout SHA used by the release workflow. How this was verified: The changed steps use actions/checkout@v4 with CI_CROSS_REPO_TOKEN, while their release-workflow counterparts use a full commit SHA.

Important Files Changed

Filename Overview
src/main/actions/verification.ts Adds read-back verification, but title-only matching can confirm semantically incorrect or pre-existing items.
scripts/actions-helper/main.swift Implements macOS native actions; optional calendar and due-date inputs can silently degrade while still returning success.
scripts/build-actions-helper.sh Builds the helper for arm64 only, breaking the explicitly supported x64 local packaging path.
src/main/actions/use-runtime.ts Composes the queue, registry, worker, gate, and platform semantic executor; Windows verification remains intentionally unexposed to chat.
src/main/tools/nativeActionToolExtension.ts Routes macOS mutations through the durable engine while keeping reads and navigation inline.
.github/workflows/windows-build.yml Adds shared package checkout/build steps, but the new credentialed checkout actions are not SHA-pinned.
src/main/actions/gate-host.ts Bridges durable engine decisions to the existing approval hook using an in-memory pending registry.
src/main/actions/use-worker.ts Drains queue entries without blocking subsequent actions while one action waits for human approval.

Sequence Diagram

sequenceDiagram
  participant U as User
  participant C as Chat tool
  participant Q as Durable action engine
  participant G as Approval gate
  participant R as Semantic rail
  participant H as Native helper
  C->>Q: Propose structured mutation
  Q->>G: Request payload-bound approval
  G-->>Q: Approve / edit / reject
  Q->>R: Execute approved action
  R->>H: Native command
  H-->>R: Result
  Q->>H: Read back observable writes
  H-->>Q: Items for verification
  Q-->>C: Done / rejected / needs help
Loading

Reviews (1): Last reviewed commit: "docs(plan): the re-cut - R2 is full rail..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

siddWednesday and others added 30 commits August 11, 2026 16:26
Research + decided direction: intents and MCP as primary action paths,
vision-based agent loop as fallback, engine as @offgrid/use in shared,
vision model as a downloadable catalog entry (GUI-Owl-1.5 / Qwen3-VL).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five phases, Aug 12 - Dec 2 2026, one demoable checkpoint per phase.
Phase 1 (semantic rail) ships standalone value by Sep 9; the vision
model install is only needed from phase 3 week 3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
16 weeks to 9 (Aug 12 - Oct 14). Code-heavy phases shrink the most
(engine 4wk to 2wk); integration-heavy phases keep slack since review,
TCC flows, and real-app iteration do not compress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sibling shared checkout + file: consumption decision added to phase 0;
mobile follows desktop as an adapter-only project on the same engine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bile adapter

Approach doc's open questions become a decision log (graph roles, eval
harness, sequencing decided; model pick narrowed to install time;
package name pending lead confirm). Plan gets an After v1 section with
the mobile adapter's prerequisites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Timeline table now shows which repo each phase builds in; dependencies
record the OGAD push-access ask (pull-only as of Aug 11, shared has push).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… re-phased timeline

From the Clawbot/product-UX research: embedded agent browser
(WebContentsView + webContents.debugger CDP, indexed snapshot, per-site
cards, takeover with capture-kill) becomes phase 3, ahead of native GUI
- zero OS permissions, no new models, shippable cut at Sep 23. Native
adapter + vision moves to phase 4, hard targets to phase 5 (ends Oct
27). Direct-reuse list added (UI-TARS sdk/ScreenMarker, nanobrowser,
nut-js fork, macos-automator-mcp, bytebot takeover pattern, Peekaboo).
Brand guidelines (off-grid-ai/brand + @offgrid/design tokens +
docs/DESIGN.md) bound as standing build rules. OpenClaw teardown
informs the zero-setup bar and the safety avoid-list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 0 of computer use. Widens the MCP-only `mcp:proposeApproval` hook
into a shared `actions:proposeApproval` seam every executor (MCP today,
computer + browser next) can gate through. Adds an ActionRisk taxonomy
(read/navigate/mutate/irreversible) with a single shouldGate() source of
truth, and per-executor riskOf() classification - a GUI click's risk is
not derivable from a tool name the way the old isActionTool regex assumed.

Backward compatible: proposeActionApproval prefers the new hook but falls
back to the legacy name via hasHook(), so a desktop-pro build that has not
yet migrated keeps gating MCP writes instead of silently running them.
MCP behaviour is unchanged (read-verb tools -> read, everything else ->
mutate), pinned by the existing queue-vs-execute dbtest plus new risk tests.

Verified: tsc (node + web), 41 unit tests, 7 dbtest, eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 0 packaging groundwork. Adds the Info.plist usage-description keys
the semantic action rail needs — Apple Events (Messages/Mail/Notes),
Calendars (+ pre-14 legacy key), Reminders, Contacts, Photos — so a
hardened-runtime build is granted each capability instead of being
refused before the OS prompt. The apple-events entitlement was already
present. Copy follows off-grid-ai/brand: outcome first, privacy as proof,
no em dashes. A source-reading test pins every key + the entitlement and
enforces the no-em-dash rule on the strings added here.

Verified: 8 new tests, YAML parses, config-reading tests green, eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 semantic rail, first slice. Adds the single seam every native
capability goes through: a Swift one-shot helper (scripts/actions-helper)
that takes one JSON command, performs a scoped EventKit action, and prints
one JSON line; and a Node invoker (runNativeAction) that resolves the
binary packaged-vs-dev like ocr.ts and parses the reply. Handled failures
(denied permission, bad args) are in-band { ok: false } results, never
throws, so the tool loop has one shape to report.

Backend covers calendar create + list to start; the switch and command
namespacing leave reminders/contacts/photos as additive cases. Pure logic
(contract, path resolution, response parsing) is split into
native-helper-logic.ts and unit tested; the Swift compiles clean under
swiftc 6.2 targeting macos13. Not yet wired to a tool or shipped in CI -
that lands with the tool that calls it, so no dead binary ships early.

Verified: tsc node, 11 logic tests, swiftc build, eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1 semantic rail, made reachable. Registers a native-action tool
extension (macOS-only) exposing calendar_create_event and
calendar_list_events to the model; create is a mutate that offers itself
to the shared approval seam (queues in pro, runs in the free build),
list is a read that runs directly. Both route through runNativeAction to
the EventKit helper. Adds 'native' to ActionKind so the approval UI and
audit can label semantic OS actions apart from GUI computer use. The
release workflow now builds + stages the helper into resources/bin, so a
packaged build ships it (self-contained; if it fails the tools report
'not available' and nothing else breaks).

Boundary injection mirrors the MCP extension, so the gate-then-run
contract is unit tested end to end without a real EventKit call.

Verified: tsc node, 90 tests across actions+tools+hooks, release.yml
parses, eslint clean on new files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
reminders_create (mutate, gated) and reminders_list (read) alongside the
calendar tools, via EventKit reminder access in the helper. Extracts a
shared formatCreated() so each create tool reuses one confirmation shape
instead of re-encoding it.

Verified: swiftc build, tsc node, 16 tool tests, eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
contacts_search (read) via the Contacts framework (CNContactStore),
returning matching names with phones and emails - the lookup the message
and event tools need to resolve a person. Read-only, so it runs without
approval.

Verified: swiftc build, tsc node, 82 tests across actions+tools, eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
messages_send (iMessage) and mail_send (Mail), both mutates gated through
the approval seam - the 'message Ali' half of the rail that composes with
contacts_search to resolve a name to a handle. Backed by AppleScript via
NSAppleScript in the helper; user-supplied values are escaped before
interpolation so a quote or backslash cannot break the script or inject
statements. Uses the apple-events entitlement + NSAppleEventsUsageDescription
already staged in phase 0.

Verified: swiftc build, tsc node, 19 tool tests (incl. a messages_send
gating case), eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
open_url opens a web page, mailto: draft, or app scheme (whatsapp://send)
via NSWorkspace - the navigate that starts the WhatsApp path and the first
use of the non-gating navigate risk class (it opens, it does not submit,
so no approval).

Verified: swiftc build, tsc node, 85 tests across actions+tools, eslint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build-mac-local.sh now builds and copies actions-helper into resources/bin
alongside the other native helpers, mirroring the release.yml step, so a
local packaged build includes the semantic-rail backend (otherwise the
calendar/messages/mail tools report 'not available').

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parseDate now tries full ISO 8601 (with timezone) first, then falls back
to the local forms a model commonly emits - 2026-08-13T15:00:00,
2026-08-13T15:00, and 2026-08-13 - interpreted in the user's timezone.
Without this a create-event/reminder call from the model would fail
whenever it omitted the timezone.

Verified: swiftc build; invalid dates still rejected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	electron-builder.yml
#	src/main/bootstrap/hookRegistry.ts
#	src/main/index.ts
Supersedes the mobile-use-replication framing. Leads with the product:
a proactive, context-grounded local assistant. Two generators (routine:
detected + demonstrated; reasoned: commitment + world-knowledge +
gap-check) feed one gated spine (resolve slots from memory -> gate shows
resolved values -> execute via rails cheapest-first). Rail hierarchy with
vision correctly placed last; three-kinds-of-seeing distinction; the
demonstration recorder; memory-grounded slot resolution; honest
reliability tiers; the moat (knows you + proactive + private + routes to
the cheapest reliable rail). Plan re-phased: reasoning engine first
(the magic, memory-driven, safe), vision fallback last. Records rail 1
as already built.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phases 2-6 now run Aug 13-26 (phases 0-1 already done). Reasoning engine
Aug 13-16, engine Aug 17-18, recorder+replay Aug 19-22, agent browser
Aug 23-24, vision fallback + hard targets Aug 25-26.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Self-contained brief for generating the demo artifacts: the product
vision, the inlined brand system (Menlo, emerald-on-black, brutalist,
dense), a five-screen day-in-the-life story (assistant home, resolved
approval card, flight nudge, record-a-routine, routines library), copy
voice, deliverable format, and the anti-patterns to avoid.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Come-ups live in Day (Needs you section, ephemeral rows - never tabs);
the gate is inline + the existing Actions queue; Routines is the one new
tab; record is a modal from it; away-presence via toast + menu-bar count.
Adds the general-engine point (no per-situation verticals - no Flights
tab) and Screen 1 now shows a mix of situations (flight + deck + renewal
+ morning brief) so the generality reads at a glance. Uses the real
left-rail nav names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Surveys who is already doing each piece of the proactive assistant:
proactive surfacing (Magic Cue, Pulse, Recall), context resolution
(Glean, Shortwave, Copilot), commitment detection (Gmail Nudges,
notetakers, MSR speech-act research), routines/teach-by-demo (Automator,
Shortcuts, RPA recorders + self-healing), and confirm-before-acting
(Operator, Manus, the false-confirmation failure). Three strategic
findings: local-first is open whitespace (Rewind/Limitless->Meta, Dot
shutdown), context-resolution-with-evidence-before-acting is unshipped
(our differentiator), and the GUI-automation reliability ceiling is real
(Mariner/ChatGPT-travel killed) - validating cheapest-rail-first.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Folds in the two parent researchers' consolidated syntheses. Routines
section gains the academic build blueprint for slot induction + self-
healing (Agent Workflow Memory, Alloy, SUGILITE/APPINITE, LUMOS AX-thesis,
hierarchical-not-flat, action-effect verification, Morae consequential-
step gating, tiered permissions, graduated trust, trycua background AX
control). Adds two lessons: detect-completion-and-auto-retire commitments
(only a local memory product can, our biggest anti-nag move) and bind the
approval card to the exact executing payload (the Alexa+ read-back bug).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ence/confidence card

Rewrites the brief from the actual app screenshots so generated artifacts
look like Off Grid AI: the real left-rail nav (with Routines added after
Actions) and its emerald active state, the real component vocabulary
(outlined buttons, pill toggles, status tags, the bottom-CTA-card shape
reused for come-ups/toasts), Menlo everywhere, flat + outlined + ~6px
radius + dotted-grid, exact dark/light tokens. Corrects the earlier
over-literal 'razor-sharp brutalist' framing that made output not look
like the app. Screen 2 now shows each resolved slot with its evidence
and a confidence tag, plus a low-confidence 'which deck did you mean'
disambiguation variant - the screen no competitor ships.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pen questions

The system design from the architecture discussion: the model proposes /
durable queue guarantees split, the Action durable record + state machine,
the reliability stack that survives a weak local model, the typed
action-handler registry + scope map (two reliability tiers), and the one
cross-platform core + adapters (mobile included). Section 7 locks the
answered decisions (incl. scope and the model-agnostic pipeline); section
8 explains the six open questions for the team - exactly-once per rail,
scheduling, trust graduation, mobile v1 target, open-core placement,
verification depth - each with options, the decision, and our lean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two layers: the assistant (brain) and the rails (actuation, behind the
DeviceController interface). Computer use = the vision rail specifically,
not the whole layer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… adapters)

An at-a-glance component diagram: generators -> the assistant (queue,
resolver, gate, router, verify) -> DeviceController port -> the rails
(semantic, browser, accessibility, vision) -> per-platform impls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In the Wednesday TRD + PRD house style: System Architecture as a C4 model
(Context L1 + Container L2), a sequence/swimlane by actor for the
send-the-deck flow, and the product user flows (Day + Chat entry -> gate
-> verify, plus the two ways a routine is born). Mermaid so they render on
GitHub / mermaid.live / an artifact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
siddWednesday and others added 22 commits August 13, 2026 17:16
… box 9)

makeUseDriver wraps a better-sqlite3-shaped handle (structural type - the
app's better-sqlite3-multiple-ciphers and plain builds both satisfy it) as
the engine's async SqlDriver. Pure: no Electron imports, injectable,
testable. One DB stays the source of truth - the engine's queue table lives
in the SAME database the app owns, not a second store.

Five integration tests at the real DB seam (dbtest suite, real SQLite file
in a temp dir, fakes only at the true boundaries): migration coexists with
app tables, a real action walks end to end with its effect landing in the
same DB and read-back verifying it, a scheduled action survives a full
engine restart over the same file, a dead worker's lease blocks a second
engine until it expires, and dedup holds across engine instances.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… box 10)

Maps the engine's closed Action types onto the Swift helper's verbs:
calendar/reminder/message/email/open to their create-send-open commands,
lookup dispatched by kind (contacts/calendar/reminders) with the
discriminator dropped, and everything else refused before the helper is
invoked - file_share and web_task belong to other rails. Pure module with
the runner injected; the Electron-bound runNativeAction attaches at wiring
time. The executor never throws: a refused mapping, a helper-reported
failure, and a thrown runner all become { ok:false, detail }.

15 tests through the injected boundary: every verb mapping, lookup kinds,
unknown-kind and unknown-type refusals, and the three failure shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…existing seam (R1 box 11)

Two contracts meet: the engine's gate AWAITS approve/edit/reject bound to a
payload hash; the app's actions:proposeApproval seam is fire-and-queue. The
bridge proposes with actionId + actionType + payloadHash on the request,
parks the decision in a pending registry, and the approval surface resolves
it via resolveActionGate(actionId, decision). Rails map to the card's
executor kinds (semantic->native, browser->browser, accessibility/vision->
computer). Free build: nothing listens, mutations keep the unchanged run-
now behaviour - and the engine still verifies and journals them. A restart
drops the in-memory registry while the Action survives at awaiting_approval
in the DB, so the next tick re-offers it - nothing is lost.

8 unit tests through the real hook registry (park/resolve, request contents,
decision passthrough, legacy-hook fallback) + 4 integration tests with the
real engine on a real DB proving the box's done-when: approve runs exactly
the approved payload (hash equality end to end), reject lands rejected with
zero device fires, an edit re-binds and the edited payload is what runs,
and the free build runs-and-verifies unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…val seam

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ounded retry (R1 box 12)

Three layers so a weak local model reliably yields a valid ActionProposal:
actionProposalJsonSchema() for llama-server's grammar-constrained
response_format with type narrowed to the handlers actually registered (the
model cannot propose what this build cannot execute); a SAP-style repair
ladder (ported idea from BAML) - raw first, then fence-strip, balanced-
object extraction that respects braces inside strings, trailing-comma drop,
bare-key quoting - where repairs only ever ADD candidates against the fail-
closed schema; and an Instructor-style bounded retry that feeds the
validation error back and treats exhaustion as rejection, never a guess.

16 tests: schema narrowing and required/extras, the balanced scanner (prose,
braces-in-strings, unclosed), one case per repair branch, unrepairable and
engine-owned-field rejections, and the retry loop's three shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The chat tool loop's mutating tools (calendar_create_event, reminders_create,
messages_send, mail_send) now become durable Actions through @offgrid/use -
validated, journaled, executed on the semantic rail, verified - and the tool
reports the REAL outcome (done / declined / needs attention / pending).
Reads and navigation stay inline (decision 5). A listening pro approval
queue keeps the legacy path byte-for-byte, so an unmigrated pro build
behaves exactly as today; the engine path activates when no hook listens
(free build), which now gets durability and verification for free.

Pieces: the gate host gains park signals (whenActionParked + a global
onGateParked); the worker races each tick against the park signal so an
action waiting on a human never blocks the queue while its outcome still
lands when the gate resolves; use-runtime composes the one production
engine (app DB driver, semantic rail over runNativeAction, gate host,
24h visibility with releaseAll stale-lease recovery at startup, heartbeat
kick for scheduled actions); the tool-to-Action-type map lives once in the
logic file. The runtime import is static - the main bundle is one CJS
chunk - and the package's CJS entry was verified to load (noble/zod/xstate
all dual).

14 new tests (park-aware drain, timeout, single-drain kick; the engine path
per outcome, dedup, refusal, pro-active legacy preservation, reads-inline)
plus the existing 28 extension tests passing untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…acOS free build

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tays external accounts

The taxonomy the user actually hit: native Mac actions were gated behind
the Connectors toggle, which means external service accounts - so a fresh
install could never discover that the assistant can act. Now ToolExtension
declares a category: 'tool' (the assistant's own on-device abilities) rides
every agentic turn; 'connector' (MCP, external accounts) joins only when
Connectors is on; an undeclared category fails closed as a connector. The
selection rule is one pure function (extension-select.ts), defined once and
tested. The composer's Tools toggle defaults ON for fresh installs - acting
on this Mac is the product's own capability, approval-gated per action -
while a user's saved preference still wins. Decision + R2 follow-up (the
cheap-first router retires the per-turn toggle; one master switch in
Settings) recorded in the plan; free-build inline-confirm question flagged
for the lead in the checklist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Calendar and reminders now verify by reading the world back after a write:
reminders.list checked for the exact title, calendar.listEvents over the
event's own range padded a minute each side (missing end defaults to the
helper's one-hour rule). Sends stay none_fuzzy and single-attempt behind
the gate - no reliable read-back exists for 'did it send', and pretending
otherwise is how double-sends happen. Everything fails closed: helper
errors, malformed results, and missing args verify false and hand the
decision to the retry policy.

buildRegistry is now exported with the run boundary injected, so the
integration test proves the done-when on the REAL registry the app ships:
a clean create verifies in one attempt; a false-ok create (helper claims
ok, nothing lands - the classic trust failure) is caught by read-back and
retried exactly once to success; a write that never lands exhausts
retry-once and asks instead of looping. 9 unit tests on the verifiers +
3 integration tests on the real engine and DB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d action (box 15)

APP-250: production app end to end - tool loop, text tool-call parsing, the
@offgrid/use engine, gate, semantic rail, read-back verification, IPC,
MemoryChat - with two fakes at the true boundaries: a scripted llama-server
that emits the tool call AS TEXT (the way small local models do) and a
scripted actions helper that records creates and answers list read-backs.
Proves on a fresh profile with no toggles touched (Tools defaults on):
ask -> tool call -> durable Action -> create -> read-back verify ->
confirmed in chat, with the helper log pinning exactly one create followed
by a list. Screenshot validated: the tool activity row shows the engine's
verified outcome. Runs in 5s.

Two harness fixes this surfaced:
- launchOffGrid strips ELECTRON_RUN_AS_NODE: a runner that itself lives
  inside Electron (VS Code tasks, agent sandboxes) exports it, and inherited
  it turns the app under test into plain Node - every spec dies with
  'Process failed to launch'.
- LaunchOptions.cwd (dev target): the native helper resolves dev candidates
  relative to the app's cwd, so a spec can plant a scripted helper in a temp
  dir; the app path arg is now absolute so it survives the cwd override.

Note: the free-build flow auto-approves (the approval card is pro's
surface), so this evidence covers the engine path; the card capture lands
with the pro migration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s; Tools default reverts to off

Three fixes, one revert - all found by actually running the gate:

- vitest.config gates the pro test GLOBS on hasPro, mirroring how the pro
  thresholds are already gated: this core checkout tracks six orphan pro
  licensing test files with no implementations beside them (pro/ is not a
  submodule here), and collecting them failed the suite for anyone without
  desktop-pro access.
- license-gate-smoke strips ELECTRON_RUN_AS_NODE like launchOffGrid now
  does: a runner living inside Electron exports it and the spawned app dies
  as plain Node before the gate can be observed.
- The composer's + trigger gains aria-label 'Composer options' (it was an
  unnamed icon button), and APP-250 enables Tools through that real menu.

The revert: Tools default returns to OFF. Defaulting it on routed EVERY
turn through the agentic pipeline (toolChat), silently switching thinking,
image, memory-scope, and project behaviour away from the plain stream -
measured as 20 behaviour-test failures, which is the everyday chat
experience changing wholesale days before a release. The category fix
stands (native actions live under Tools); discoverability is the composer
hint (checklist 18b); the real always-available answer is R2's per-turn
router. Fast suite: 279 files green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
startModelServer is async (it awaits pickFreePort and listen); two tests
called it unawaited and fetched immediately, passing only when the event
loop happened to run listen first. On this machine's scheduling the fetch
consistently won (ECONNREFUSED in 4ms) and the whole db-suite coverage
report was suppressed with it. One word at two call sites; 7/7 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gate's own measure drove these: branches 49.7->60.3 (floor 57) and
functions 41.8->60.0 (floor 52) on the code this branch adds, with
statements/lines at 99.5.

- use-runtime.integration.dbtest: the real composition on a real DB with
  only electron and the native helper mocked - lazy singleton, a reminder
  end to end through the real worker, waitForOutcome timeout, and the
  approval-hook probe across both hook names.
- use-driver unit: the reader/non-reader routing matrix on a structural
  fake (the real-SQLite behaviour stays proven in the dbtest suite).
- native-helper unit: candidate-miss, first-existing-candidate, the
  non-zero-exit-with-stdout salvage, and spawn failure - all degrade to
  reported failures, with electron and child_process mocked at the boundary.
- gate-host: park-signal corners (immediate and later whenActionParked,
  onGateParked subscribe/unsubscribe, pending count + abandon).
- extension: the spec-table completeness sweep (every title/buildArgs/
  formatResult), schemas + systemHint, and the edited/poisoned/no-detail
  outcome arms.
- emit: escaped quotes inside balanced-object strings; helper-logic: long
  invalid line truncation; semantic-rail: lookup with no kind; verification:
  unparseable calendar start never lists.
- devices-sync e2e: the pro implementation import loads lazily behind
  PRO_PRESENT - a static import failed spec collection in a core-only
  checkout before the guard could skip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Core carried pro/main/licensing/__tests__ (tests only, no implementations)
even though .gitignore already ignores /pro/ - tracked files override the
ignore. With desktop-pro access restored and the files verified byte-
identical there, core stops tracking them: pro/ is the desktop-pro clone
(the submodule-style layout CLAUDE.md describes), fully ignored by core.
This is also what made the fast suite collect orphan tests in core-only
checkouts; the hasPro glob gate covers anyone without access.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…id/use

The first Windows CI run of the engine branch (31778949364) failed its
typecheck on 'Cannot find module @offgrid/use' and '@offgrid/sync/portable':
windows-build.yml predates the shared adoption and never checked out
../shared at all, and release.yml (both build-mac and build-win) builds
only packages/sync - file-dep prepare alone does not emit @offgrid/use's
dist types before the typecheck. windows-build now mirrors release.yml's
shared checkout (matching-branch ref, main fallback, root ci) and all
three shared steps build packages/use alongside packages/sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…x 17)

Calendar, reminders (tasks), and mail run through LOCAL Outlook COM via
PowerShell - the write lands in Outlook's local store and syncs when the
network returns, matching the mac EventKit/Mail behaviour instead of
failing offline like a cloud API. The scripts print the same one-JSON-line
contract the mac helper speaks, so parseHelperResponse is shared. open goes
through Electron's shell; message is refused honestly (iMessage is macOS-
only). Microsoft Graph is the online-only fallback for setups without
local Outlook: the port and fallback logic ship boundary-tested (only
consulted when COM says not-registered AND the port reports signed-in);
the OAuth wiring is a recorded fast-follow, so production passes no port
and the failure names both paths.

The runtime picks the rail by platform in exactly one place; a DSP test
drives both rails through one dispatch with zero caller changes. 21 tests:
script building and quoting (embedded quotes doubled), the COM-unavailable
matcher, every mapping and refusal, ordinary-error vs absent-Outlook vs
signed-out-Graph vs Graph-failure paths, and the never-throws guarantee.
Follow-ups (win tool exposure, Outlook read-back verbs, Graph sign-in)
recorded in the checklist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Run 31779453356: shared checkout resolved at the matching branch,
@offgrid/use built, typecheck + bundle passed, NSIS packaged, 414MB
installer artifact uploaded. Cert + real-Windows model-load smoke remain
recorded release items.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 280a00b7-9c09-46be-8145-a3f02a5c3b36

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

siddWednesday and others added 2 commits August 14, 2026 13:25
CI's dependency-boundaries gate caught the circle the category seam
introduced: tools.ts imports selectToolExtensions, and extension-select
imported ToolExtension back from tools.ts (a type import still counts).
The selector only needs the category field, so it now asks for exactly
that - a structural CategorizedExtension, generic over the caller's richer
type - and imports nothing from tools.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… and routines follow

The lead's steer plus R1 field feedback, in the build doc: R1 is done (PRs
open); R2 (~5-6 days) ships all four rails chat-driven on both platforms -
Windows exposure, the browser rail, the supervised vision rail (UI-TARS-
1.5-7B) - plus Approval UX v2: inline approval cards in chat, outcome
feedback everywhere via the pro approval-executor migration to the engine
gate, and risk-tiered gating (reversible mutations auto-run with verified
confirmation + Undo; sends keep the gate). Notices-you moves to R3,
routines to R4. The R1 checklist records the three pro-path UX verdicts
that drove the approval rebuild. One versioned release ships after R2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@siddWednesday
siddWednesday marked this pull request as ready for review August 14, 2026 09:09
@siddWednesday
siddWednesday merged commit 8f887f6 into main Aug 14, 2026
2 checks passed
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

Comment on lines +38 to +43
)
}

const HOUR_MS = 60 * 60 * 1000
const PAD_MS = 60 * 1000

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Title-only verification confirms wrong effects

When a requested calendar is unavailable, a due date is invalid, or a same-title item already exists, read-back accepts title equality alone, causing the engine to report completion even though the event is in the wrong calendar, the reminder lacks its due date, or the requested write did not land.

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SRC="$ROOT_DIR/actions-helper/main.swift"
OUT="$ROOT_DIR/actions-helper/actions-helper"
swiftc -O -target arm64-apple-macos13.0 -emit-executable "$SRC" -o "$OUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Arm64 helper breaks x64 builds

When an Intel Mac runs the supported local packaging or development path, this fixed arm64 target places an incompatible helper in the x64 application, causing every native calendar, reminder, contact, message, mail, and URL action to fail at process launch.

- name: Checkout shared at the matching ref
id: shared_branch
continue-on-error: true
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security Mutable action handles build credentials

The new shared-repository checkout uses mutable actions/checkout@v4 while receiving CI_CROSS_REPO_TOKEN, leaving branch packaging credentials and installer output exposed to an upstream tag repoint; pin this step and its fallback to the immutable checkout SHA already used in release.yml.

How this was verified: The changed checkout steps pass CI_CROSS_REPO_TOKEN to actions/checkout@v4, while the equivalent release steps use a full commit SHA.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant