Skip to content
STAIxBWLBPublic

About

Local-first AI workspace desktop app for Korean knowledge/document operations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

847 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Maru

Maru is a local-first desktop workspace for Korean knowledge and document operations. It combines a React 19 and TypeScript interface with a Tauri 2 Rust core, and treats the filesystem as the source of truth.

The current product release is v1.1.21, Even Footing. Releases before v0.3.0 shipped under the name Anchor; v0.3.0 completed the application identifier and on-disk migration to Maru.

Current Status

Area State Evidence
Product release v1.1.21 Signed desktop bundles and standalone CLI for macOS, Windows, and Linux
Planning milestone v1.1 Felt Quality and Native Proof, closed Closed 2026-09-27: phases 6-11 (56 plans), audit tech_debt with the debt accepted
Application shell Complete 19 lazy modes; MainApp held to 15 useState and 24 useEffect calls
Verification Passing Typecheck, ESLint, unit tests, Rust fmt/clippy, E2E, build, and bundle budgets
Typed IPC ERR-06 closed Every conflict-emitting command preserves { code, message }; recursive source guard active
Main-thread isolation PERF-01/02 closed All 388 production commands classified: 379 isolated off the UI thread, 9 kept on it for main-thread affinity; Phase 08 baseline native load proof keeps loaded p95 at 2ms with the negative control at 4789ms
Active milestone None, v1.2 being defined v1.1 closed 2026-09-27; opening v1.2 moves releases to 1.2.0

The milestone archive, audit, retrospective, and summary live under .planning/milestones/, .planning/RETROSPECTIVE.md, and .planning/reports/.

Core Principles

  • Filesystem authoritative: notes, tasks, drafts, evidence, and diagrams remain usable without Maru. Caches are disposable.
  • Local-first writes: ordinary editing happens inside user-owned workspace folders. Cloud or Hub writes require an explicit supported path and approval.
  • Byte-stable documents: a frontmatter field edit preserves unrelated keys, comments, order, quoting, and document body bytes.
  • Fail-closed mutation: revision checks, workspace ownership, write policy, path containment, and managed-write validation run again in Rust.
  • Inspectable automation: AI work is suggestion-first. Protected writes use approval staging and durable audit events.
  • Korean document fidelity: HWPX, DOCX, PDF, Korean filenames, Korean IME, and public-document writing workflows are first-class concerns.

Rule SSOTs for the working environment live at:

~/workspace/work/_meta/rules/
  frontmatter-schema.md
  document-lifecycle.md
  hub-contract.md
  evidence-policy.md

Work Surfaces

Settings opens as an overlay and is not counted as an app mode. Diagram and Graph are enabled by default; E2E Flow is flag-gated.

Mode Korean label Purpose
dashboard 대시보드 Today, tasks, schedule, inbox, agents, drafts, git, recent documents, and sync status
pkm 문서 Markdown and HTML document tree, multi-tab editor, outline, references, and utility rail
scratchpad 스크래치패드 Durable memos and disposable result files with Rich, Source, and Preview editing
files 파일 Finder-style folders, direct children, previews, editing, and safe file operations
inbox 인박스 Drop, pending, processed, provider, classification, approval, and processing flows
comms 메시지 Telegram, Outlook/Microsoft 365, and provider readiness configuration
meetings 회의록 Plaud and external meeting-note review, transcript references, meeting review, and follow-through
today 오늘 Prepare, execute, review, capacity, calendar selection, and explicit sync
tasks 태스크 File-backed task list, calendar, detail editing, status changes, and AI runs
drafts 아이디어 Idea lifecycle, implementation drafts, promotion, and recurring automation
gap 갭 분석 Compare promoted drafts with frozen baselines and record human revision patterns
agents 에이전트 Runtime status, chat, runs, permissions, schedules, and user-created agents
catalog 카탈로그 Operations catalog for deadlines, approvals, evidence, and inbox signals
studio 스튜디오 Seven-step document authoring, template, guideline, HWP field, export, and package flow
diagram 다이어그램 Concept maps, report patterns, templates, history, and managed report assets
architecture 설계도 Read-only gallery of archify blueprints (*-rendered.html) from the working trees of dev/ and sites/ submodules
graph 그래프 WebGL vault/workspace graph, neighborhoods, saved views, and reviewed relationship writes
sites 사이트 Site switcher and embedded native browser surface
e2e E2E 플로우 Hidden end-to-end flow console for development and verification

Install

The desktop application and standalone CLI are separate artifacts.

brew tap STAIxBWLB/homebrew-cask

# Desktop application only
brew install --cask maru-workspace

# Standalone CLI only; installs the executable as `maru`
brew install maru-cli

maru --version

The cask installs Maru.app and does not create a CLI symlink. The desktop app uses signed Tauri updater metadata from GitHub Releases. Homebrew installations can also be upgraded explicitly:

brew upgrade --cask maru-workspace
brew upgrade maru-cli

Architecture

+---------------------------------------------------------------+
| Tauri WebView: React 19 + TypeScript                          |
|                                                               |
| 19 typed lazy mode adapters                                   |
| BlockNote / CodeMirror / DOMPurify / Radix UI                 |
| Sigma WebGL / Graphology / diagram canvas / terminal canvas   |
+-------------------------------+-------------------------------+
                                | Tauri IPC
+-------------------------------v-------------------------------+
| Rust core                                                     |
|                                                               |
| workspace scan + cache       document + frontmatter           |
| inbox + provider bridges     today + tasks + scheduler        |
| terminal PTY + screen model  graph + diagram + Studio         |
| skill host + agent host      Hub client + export pipeline     |
| write policy + approval      atomic files + revision guards   |
| worker-isolated commands     path-transaction admission       |
+---------------+-------------------------------+---------------+
                | stdio                         | fixed argv
+---------------v--------------+  +-------------v---------------+
| Local MCP sidecar (Node)     |  | External CLIs and skills    |
| Read-first workspace tools   |  | Claude/Codex/Kimi/Kiro/hwp  |
+------------------------------+  +-----------------------------+

Module Boundaries

  • Rust owns workspace filesystem access, cache, git operations, frontmatter, provider bridges, terminal sessions, skill ownership, agent execution, catalog, Studio, export, Diagram, Graph storage, and write enforcement.
  • src/lib/ owns frontend domain logic and module stores.
  • React components own rendering, editors, user interaction, graph layout, and diagram canvas behavior. They do not bypass Rust write guards.
  • src/lib/ does not import components, except for documented type-only legacy boundaries. Nothing imports src/App.tsx.
  • Shared UI state follows keyed module-store plus useSyncExternalStore patterns. No additional global state library or provider tree is used.
  • Production commands never block the UI/shared async worker thread: 379 ISOLATED commands run on awaited spawn_blocking workers and 9 native-window commands stay UI-bound, and every filesystem mutation passes shared path-transaction admission before taking domain locks. The 388-command inventory, worker-boundary, and admission evidence are gated by check-command-isolation in make verify.

Capability Highlights

Documents and Files

  • Markdown uses Rich, Source, and sanitized Preview modes.
  • HTML uses Visual, Source, and sandboxed Preview modes. Scripts, event handlers, forms, frames, meta refresh, network resources, and paths outside the owning workspace are blocked in the runtime clone and never written back.
  • Documents support read, save, create, version, rename, move, duplicate, and system-Trash operations with optimistic concurrency.
  • Deleting a document opens a review of every file it would move to the system Trash: the source, plus, when chosen, derived files (export bundle, version snapshots, Studio outputs) and Maru metadata (Binder, Studio, kg-cache) that record a link to it. A plan that changed since review deletes nothing.
  • Files presents folders, direct children, search, binary previews, shared document drafts, multi-selection, keyboard control, and collision-safe file operations.
  • Rename and move use .maru-rename-txn/ staging with recovery on the next scan. Operations never overwrite an existing target.

Korean Document Operations

  • Document Studio guides source selection, template and guideline choice, section editing, HWP fields, export, and package freeze.
  • HWP fields, form fill, the inline HWPX preview, and HWPX structure checks run through released hwp 1.3.0 or newer. Native template publication fails closed on malformed fill reports, unmatched fields, or validation failure.
  • The lower-level hwped_* engine bridge supports read, render, edit, compose, validate, and capabilities with hwp 0.8.7 or newer.
  • Export manifests bind source hashes to DOCX, HWPX, and PDF outputs and record partial failures instead of reporting silent success.
  • The gaejosik linter supports Korean public-document style during authoring.

Evidence, Drafts, and Knowledge

  • Plaud and other externally generated meeting notes enter a mandatory source review before meeting-note generation. The review preserves the imported original, supports participant and meeting-context correction, shows a side-by-side original-versus-corrected diff with aligned rows and shared scrolling, and records explicit version reasons and acknowledged uncertainties. A transcript is optional evidence, read-only by default; Maru does not connect to the Plaud API in this workflow.

  • Evidence Binder stores schema-v2 state under <workspace>/.maru/binder/<doc-id>.json, uses full binary SHA-256 identity, revision-checked atomic mutations, explicit targets, local verification, and submission selection.

  • Drafts use $MARU_DRAFTS plus .maru/drafts/index.json; promotion is approval-gated and freezes a baseline for Gap analysis.

  • Diagram documents live at diagrams/*.cmd.json; report assets live under attachments/diagrams/<docId>/; pattern presets live in .maru/diagram-patterns/.

  • Graph uses a multi-directed Graphology model, Sigma WebGL, an off-thread ForceAtlas2 worker, visibility reducers, saved views, and schema-gated writes.

Terminal and AI Runtimes

  • The terminal uses portable-pty and alacritty_terminal, streams ordered generation-tagged frames, limits frames in flight, and requires a current generation-bearing handle for every session command.
  • Terminal and Graph share a persistent bottom/right panel with independent themes and remembered layout.
  • Claude Code, Codex, Kimi, and Kiro are first-class runtimes. Each named agent binds a skill, runtime, permission mode, and optional schedule.
  • Adaptive runtime policy is opt-in under Settings > AI (ai.adaptivePolicy). Each invocation asks dot ai policy resolve --json for an eligible runtime, model, reasoning effort, and native automatic-review permission. Missing or invalid policy fails visibly; it never falls back to permission bypass. Claude and Codex with verified subscription authentication can execute this policy; DGX execution is excluded until endpoint and auth binding is verified. Kimi and Kiro remain available for existing manually configured runs. Legacy schedules and the commit-message API retain their selected runtime and do not inherit adaptive policy automatically. Explicit plan mode retains native restrictions on task-file writes; it is not a blanket guarantee that every remote MCP operation is read-only. The authorized memory/vault lookup and record exceptions receive exact no-confirmation rules, subject to higher-level native mode restrictions and explicit deny rules. No other remote write permission is added. Runtime decisions are reported in invocation metadata and chat diagnostics. Structured roles freeze the complete initial configuration and stop on drift. Chat permits at most two configuration changes per task, then stops on another change; explicit selections freeze the recorded configuration. New turns resolve again using capped transcript replay, not native session resume. Maru does not automatically replay failed actions or change providers after a rejection. Explicitly authorized claude-mem and Obsidian lookup/record tools receive exact native approval rules from resolved knowledge_approvals; unknown servers, tools, deletion, and move grants are rejected. Knowledge approval scope is part of the frozen task fingerprint. A runtime switch must retain every previously available memory/vault tool or stop before launch, even when switch budget remains. Vault writes remain MCP-owned. Native configuration remains owned by dotfiles; Maru only constructs argv.
  • Provider probes and real integrations have bounded output, timeout, cancellation, and stale-request handling. The real-binary integration smoke remains separate from hermetic make verify.

Skills and Workspace Sync

  • Proposal apply approvals bind the canonical workspace target, proposal payload, source run, file revisions, and workspace policy revision. Rust recomputes the binding inside the write transaction; drift needs a new approval. Bound grants are single-use, terminal decisions are immutable, and remembered grants match the complete binding. Request, decision, consume, and effect outcome records live under ~/.maru/approvals/<canonical-workspace-sha256>/ as application-owned private metadata, independently of provider document capabilities. Legacy registry migration completes before the approval policy pin. Restarting restores readable history without restoring authority. Failed durable consume blocks writes. An outcome audit failure after an effect reports uncertainty and must not cause automatic retry.

  • The skill host owns five tiers: core, public, private, imported, and managed. One name maps to one tier; doctor, dirty, reconcile, import, and tool-sync operations are available through the CLI.

  • Codex installs target $CODEX_HOME/skills when CODEX_HOME is set, otherwise ~/.codex/skills.

  • Settings > Jobs manages the external dot workspace sync service through its versioned JSON API. Maru uses fixed arguments, serializes mutations, and confirms destructive or secret-expanding actions.

  • Workspace calendar jobs default to their local-time daily calendar fire. recoveryMode: "repeat" keeps the legacy StartInterval behavior; recoveryMode: "missedFire" keeps the calendar cadence and adds an interval guard that runs only when neither a successful run nor the install baseline covers the latest daily fire. The baseline is stored before launchd loads the jobs, and run state (including the effective Jobs > Start/Stop state) is kept under .maru/jobs-state/. Install sets the initial enabled state and Start or Stop changes it under a short state lock, separate from the lock held across a running child. Run now queues a one-shot request and returns after launchd kickstart; launchd retains control of stopping the child. Run now requires the installed service to be loaded and enabled, and bypasses daily fire dedup for that one request. The ledger attributes each success to the daily fire frozen at logical run admission, so a long run crossing tomorrow's fire cannot suppress tomorrow's work. Calendar and recovery invocations share a run lock and success ledger, so a recovered fire is not run again if launchd later coalesces its calendar event. This schedule model supports daily times; it does not express weekday or weekly calendars.

  • Every installed job runs through the CLI wrapper, including Repeat jobs, with its existing schedule preserved. The wrapper durably records admission before spawning and records native owner/child creation identities and process exit separately from the success-fire ledger. Jobs shows the latest 100 receipts; recovery skips, deduplicated fires, and manual requests remain distinguishable. No verifier is configured by this feature, so verification is notRequested, including for exit 0. Interrupted runs with uncertain child ownership block another effect attempt; history reconciliation never replays a provider.

Storage and Configuration

Global user state:

~/.maru/
  settings.json
  workspaces.json
  skills/registry.json
  skills/_cache/

Workspace-local state:

<workspace>/
  .maruignore
  .maru/
    cache/                  # disposable workspace index and Hub cache
    workspace-state.json   # collapsed folders and workspace UI state
    versions/               # explicit document snapshots
    studio/                 # per-document Studio state
    binder/                 # per-document Evidence Binder state
    diagrams/               # diagram history and backups
    meetings/source-reviews/ # immutable Plaud originals, state, versions, and review records
    drafts/                 # draft index and frozen promotion baselines
    queue/                   # recoverable provider/Hub work queues

<workspace>/.maru/settings.json is a legacy migration input only. New workspaces use global settings plus workspace-state.json.

Scratchpad structure:

<work>/scratchpad/
  ideation/{seeds,developing,proposals,_archive}/
  memos/
  drafts/
  temp/{claude,codex,kimi,kiro,runtime}/

Only temp/ is disposable. Ideation, memos, and drafts are durable and may be Git-tracked. Cleanup is explicit and moves selected files to system Trash.

Public workspace configuration is registry-only in v1.1.0. Provider metadata is non-secret, manually entered roles map to coarse capabilities, and filesystem writability is probed again before granting direct writes. OAuth and live cloud role checks are not implied by this metadata.

Meeting source review accepts pasted UTF-8 text and TXT/Markdown imports up to 2 MiB per source. Imported bytes are retained as immutable originals before any editing; unsupported or undecodable files are rejected without lossy conversion.

Safety Contracts

  • src-tauri/src/frontmatter/ops.rs is the only allowed frontmatter write path.
  • resolve_inside_vault and shared containment helpers stay lexical. Deliberate symlinks inside a workspace remain supported.
  • Managed writes pass vault_guard::validate_managed_write, create a snapshot, and use revision-checked atomic replacement. Note deletion remains MCP-only.
  • Every conflict code the frontend can consume crosses as structured IpcError. New Rust modules are covered automatically by the ERR-06 guard.
  • Error normalization accepts only known contract codes. Unknown or forged codes degrade to a plain Error and never satisfy recovery branches.
  • Provider and subprocess commands use fixed argv rather than a shell whenever input can cross a trust boundary.
  • The application has no default telemetry, Maru account, cloud-sync engine, multi-user CRDT, or autonomous-write default.
  • Signed update metadata is mandatory. Unsigned or ad-hoc updater feeds are not accepted.

Development

Requirements:

  • Node.js 22 or newer
  • pnpm 9.15 or newer
  • Rust MSRV 1.89.0; rust-toolchain.toml pins the repository verification toolchain
  • Platform libraries required by Tauri 2

Common commands:

pnpm install

# Browser development with mocked Tauri IPC
pnpm dev

# Native development
pnpm tauri:dev

# Focused frontend gates
pnpm typecheck
pnpm lint
pnpm lint:i18n
pnpm test
pnpm test:e2e
pnpm build

# Rust gates
make test-rust
make fmt-check
make clippy

# Complete hermetic verification
make verify

# Phase 08 evidence closure gate alone (388 production commands, PERF-01/PERF-02)
node scripts/check-command-isolation.mjs --all --expected-count 388

# Full verify plus release-only CLI and debug Tauri checks
make release-checks

# Complete local release gate
make release-preflight

# Real installed runtime smoke; not hermetic
make verify-integration
MARU_CLI_SMOKE_ROUNDTRIP=1 make verify-integration

# TypeScript + Rust coverage report; non-gating, not part of verify, needs
# cargo-llvm-cov and llvm-tools installed locally
make coverage

# Local MCP sidecar smoke
MARU_MCP_WORKSPACE="$PWD" node sidecars/maru-mcp/index.mjs

Skill registry checks:

cargo run --manifest-path src-tauri/Cargo.toml -p maru-cli --bin maru-cli -- --version
cargo run --manifest-path src-tauri/Cargo.toml -p maru-cli --bin maru-cli -- doctor --json
cargo run --manifest-path src-tauri/Cargo.toml -p maru-cli --bin maru-cli -- skills dirty --json
cargo run --manifest-path src-tauri/Cargo.toml -p maru-cli --bin maru-cli -- skills sync --check --tools claude,codex --json

Selected-agent skill federation

Maru owns generic skill sources, registry records, and deployment. External orchestrators such as dotfiles query the native CLI before requesting a selected operation (Maru issue #396, dotfiles-v2 issue #150):

maru skills capabilities --json
maru skills list --json
maru skills sync --check --tools claude,kimi --skills meeting-notes --json
maru skills sync --apply --tools claude,kimi --skills meeting-notes --json

The capability response has schemaVersion: 1, targets containing claude, codex, kimi, qwen, grok, and opencode, and selectedSync: true, list: true. list returns flat SkillRecord fields plus installable: boolean and an optional reason for unavailable entries. The picker must offer only installable entries. Eligibility uses the same target-independent checks as selected sync: Maru-owned source, valid metadata, and existing source/skill paths. Target conflicts and profile divergence remain sync-preview checks. These list-only fields are never stored in the registry. Listing proceeds without refreshing sources, persisting migrations or writing the registry, or materializing bundled skills. Missing capability support requires the orchestrator to defer; it must not copy files itself.

--skills accepts comma-separated unique names or exact IDs. Empty selectors, ambiguous names, catalog duplicate-name conflicts (including selection by ID), missing sources, native-plugin skills, and external inventory skills are rejected. Explicit selections reconcile only those skills and targets, preserving unrelated installs. Omitting --skills retains the existing whole Maru-owned catalog sync behavior. Neither command updates upstream sources. Conflicting directories or foreign links are preserved and reported as errors. The sync JSON report adds profiles (the resolved destination root per requested target, so the orchestrator can scope subprocesses to the same profile environment) and a source on every planned action (what the link points at) alongside the destination path. --check writes nothing.

Default skill roots are ~/.claude/skills, $CODEX_HOME/skills (default ~/.codex/skills), $KIMI_CODE_HOME/skills (default ~/.kimi-code/skills), ~/.qwen/skills, ~/.grok/skills, and $OPENCODE_CONFIG_DIR/skills (default $XDG_CONFIG_HOME/opencode/skills, then ~/.config/opencode/skills). Overrides must be absolute paths. Recorded install roots remain sticky; apply and selected sync fail when the runtime home changes unless --retarget is explicitly supplied. A selected retarget can leave unrelated installs pinned to an earlier root. Sync rejects targets spanning multiple effective roots until an explicit unfiltered --retarget reconciles them. Read-only whole-catalog previews of a single sticky root report that root, including when the runtime home differs. Orchestrators must never add that flag automatically. A destination link proves file exposure, not that a particular agent release loaded the skill.

Install/apply remains subject to per-repository resource admission and host-pressure checks. Global shared-tooling maintenance is serialized independently. An orchestrator must hold the applicable admission lease for the complete CLI call.

Verification and CI

make verify covers:

  • four TypeScript projects: application, Node config, E2E, and scripts
  • ESLint correctness rules with zero warnings
  • release-version synchronization and static architecture guards
  • frontend tests and Rust library tests
  • rustfmt and clippy with warnings denied
  • production frontend build and gzip bundle budgets, including the native-e2e ship-isolation scan of the produced bundle (D-10)
  • the Phase 08 evidence closure gate (check-command-isolation): every registered production command carries final justified worker-boundary, mutation-admission and processing-caller evidence against the 388-command inventory (node scripts/check-command-isolation.mjs --all --expected-count 388)

On Windows, run Make with Git for Windows' Bash and Unix tools on PATH, alongside Node, pnpm, and Cargo. For a native Windows GNU Make installation, use make verify "SHELL=C:/Program Files/Git/bin/bash.exe" (and the same override for make test-e2e). Tracked text checks out with LF via .gitattributes; this preserves vendored pin hashes and Node shebang imports. The Windows unit and browser test runners default to two workers to reduce startup contention on hosts with many logical CPUs. Vitest's explicit --maxWorkers and Playwright's --workers options still override that default.

Use workspace-relative paths such as task_management.root: tasks in shared workspace.config.yaml files. A home-relative or absolute path from another machine can resolve outside the selected workspace and is correctly rejected by the containment guard.

Pull requests run a lightweight decision job first. Source changes fan out to make verify and Playwright E2E. Version-changing PRs run make release-checks instead of the ordinary verify target, adding release-mode CLI and debug Tauri checks. Documentation-only and .planning/** changes skip expensive CI.

A push to main may reuse the exact PR tree only when the successful PR run is for the identical head SHA. Direct pushes, merge-tree differences, missing checks, and API failures run the full suite.

CI E2E runs Chromium against Vite with mocked IPC. It does not prove WKWebView, the native PTY, Korean IME behavior, macOS menus, signing, or notarization. macOS-affecting changes require a real-app or release-artifact check.

Native fixture lifecycle and terminal readiness are documented in docs/native-e2e.md. Spec-owned roots/profiles are isolated at the launcher boundary; actual Mocha setup hooks and final completion gates propagate fixture failures.

Coverage is measured by make coverage locally and by the coverage workflow on every push to main. It never runs for pull requests and is not a required check. Both HTML reports upload as the coverage-report artifact with 30-day retention, and the per-language and per-crate totals table appears in the run's Job Summary. There is no threshold, so a lower number never fails anything.

The compiled CSP gate invokes the built host-native desktop binary with --print-compiled-csp. This headless diagnostic reports the runtime-effective CSP from the same generated Tauri Context used by startup (including devCsp fallback when applicable), before plugins, services, or windows initialize. The guard rejects absent or malformed policies, unsafe script directive fallbacks, execution failures, and timeouts. Binary string layout and embedded source JSON are not evidence of the effective policy.

Generated Rust executable test fixtures use src-tauri/src/test_support.rs::write_executable_fixture, including rewrites, with the existing script bytes and permission mode. On Unix the helper waits for all inherited writable file descriptors to close before direct execution. Linux can otherwise return ETXTBSY even after fs::write closes its own file: a concurrent fork can retain a writable descriptor until exec. The test-only lock handoff addresses fixture publication without changing production spawn behavior or test assertions. Windows cannot execute a #! script, so the helper also places a <path>.exe launcher beside it, which Command::new(path) and PATH lookup resolve first. The launcher runs the script under its shebang interpreter (Git for Windows sh/bash, or node), quotes every argument for MSYS, and passes \\?\C:\ paths in their equivalent C:\ form. It is built once per run with the toolchain's rustc. Fixtures that run a shell line directly use test_support::posix_shell(), and fixture Git remotes and paths given to Git use test_support::git_local_path(), because Git reads a verbatim path as an SSH remote.

React dialog fixtures use src/lib/testing/unmountReactRoot.ts before removing their fixture containers. Radix FocusScope defers disposal to a timer; awaiting it inside the fixture's React act keeps CustomEvent construction in the same jsdom realm. Closing that realm first can leak an invalid Event into the next test file even when all assertions passed.

Release Process

The release version's major and minor come from the active GSD milestone in .planning/STATE.md; releases only increment the patch. Milestone v1.1 shipped as 1.1.x and closed on 2026-09-27. Opening milestone v1.2, whose scope is being defined, moves releases to 1.2.0. There is one tag namespace and it belongs to releases: milestone completion no longer creates a git tag.

Version sources must remain synchronized:

package.json
src-tauri/tauri.conf.json
src-tauri/Cargo.toml
src-tauri/maru-cli/Cargo.toml
src-tauri/Cargo.lock (maru and maru-cli package entries)

Release sequence:

  1. Merge a version PR after release checks and Playwright E2E pass.
  2. Verify exact-tree main CI.
  3. Dispatch and pass Release Preflight.
  4. Publish a GitHub Release whose tag is exactly v<package version> and whose target is the verified main commit. Validate release inputs enforces the prefix and fails before release lookup or bundle creation.
  5. Wait for Release Bundles to finish across macOS ARM, macOS Intel, Linux, and Windows.
  6. Verify the public artifacts, updater manifest, signatures, and Homebrew tap.

The release workflow produces 20 platform assets, then a single finalizer publishes latest.json for 11 updater platforms and updates Homebrew. A complete release therefore has 21 non-empty assets. Platform jobs never race to write the manifest.

Useful local checks:

make release-version-check
node scripts/check-release-version.mjs --tag v$(node -p "require('./package.json').version")
make macos-distribution-check
make macos-distribution-local-check

macOS Signing and Notarization

Public macOS releases fail closed unless all signing secrets are configured:

APPLE_CERTIFICATE
APPLE_CERTIFICATE_PASSWORD
KEYCHAIN_PASSWORD
APPLE_ID
APPLE_PASSWORD
APPLE_TEAM_ID
TAURI_SIGNING_PRIVATE_KEY
TAURI_SIGNING_PRIVATE_KEY_PASSWORD

Developer ID signing and Tauri updater signing are separate. The normal release does not enable the browser-passkey provisioning overlay.

Local Apple material belongs outside the repository:

~/workspace/work/.maru/secrets/apple/
  DeveloperIDApplication.p12
  AuthKey_<APPLE_API_KEY_ID>.p8
  certificate-password
  api-issuer-id
  api-key-id             # optional
  keychain-password      # optional

After downloading release artifacts:

xcrun stapler validate Maru_*.dmg
spctl -a -vv -t open --context context:primary-signature Maru_*.dmg
codesign --verify --deep --strict --verbose=4 Maru.app
spctl -a -vv -t exec Maru.app

Homebrew verification:

make homebrew-audit HOMEBREW_TAP_DIR=../homebrew-cask
make homebrew-fetch HOMEBREW_TAP_DIR=../homebrew-cask

The explicit make homebrew-update* targets are recovery tools. The release finalizer normally updates STAIxBWLB/homebrew-cask automatically after the manifest succeeds.

Skills OTA Channel

Skills deploy independently from the desktop application through STAIxBWLB/skills. Bundle changes are verified, packaged, minisign-signed, and published to the skills-channel prerelease. Maru checks after launch and every six hours, auto-applies only when local skills are clean and runtime-compatible, and exposes manual check/apply commands in the CLI and Skills UI.

src-tauri/skills-bootstrap/ is a frozen first-run fallback, not the live OTA source. Refresh it deliberately with make skills-bootstrap-refresh only when an application release must carry a newer offline bootstrap.

Roadmap

The GSD v1.0 Structural Debt Paydown and v1.1 Felt Quality and Native Proof milestones are complete and archived. v1.1 (phases 6-11) closed on 2026-09-27 with audit status tech_debt, and the debt was accepted. The next milestone, v1.2, is being defined and is not open yet. The long-range product plan remains in ROADMAP.md, but planned items there are not active commitments until a GSD milestone promotes them into requirements.

Milestone v1.1 promoted part of the carried-over backlog into requirements. The remaining candidates are:

  • ERR-05 closed-enum IPC construction
  • Hub evidence index, approval/finalize, certification, and Deck Studio tracks

Scope Boundaries

Maru v1.1.0 intentionally does not include:

  • semantic or embedding search
  • a Maru account or default telemetry
  • a built-in cloud-sync engine
  • mobile distribution
  • iMessage or Slack ingestion
  • multi-user collaboration, CRDT, or realtime editing
  • PDF annotation or OCR
  • a public skill marketplace server
  • agent-autonomous edits as the default behavior
  • unsigned updater feeds

Documentation

License

No license file is currently published. All rights reserved unless a license is added.

About

Local-first AI workspace desktop app for Korean knowledge/document operations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages