feat(studio): native Tauri app — Phase 1 (shell + Rust pure-state session)#13
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ied) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atures Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add apps/studio/README.md with Tauri native dev section - Fix two clippy lints in session.rs (new_without_default, manual_map) - Add **/target/** to .prettierignore to prevent prettier scanning Cargo build artifacts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eared fixture
- set_persona("") now keeps the key present as "" with identity: true (mirrors TS Brain.setPersona verbatim store)
- set_readme(" ") now drops the key (mirrors TS trim-empty clears)
- emit-fixtures.ts emits state-cleared.json: canonical mutations → setPersona('')+setReadme(' ')
- Extended TS fixtures.test.ts (38 tests) and Rust fixtures_test.rs (6 tests) with cleared-state case
- Added Phase 1 avatar scope comment in session.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add studio-rust job: macos-latest, dtolnay/rust-toolchain@stable, Swatinem/rust-cache@v2, cargo test + cargo clippy -D warnings. Sources cargo env in each step for hermetic shell init. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements Task 1 of Phase 2 native Studio app: - Rust core module (core/bundle.rs, core/digest.rs) with canonical_digest matching the TS implementation byte-for-byte (sha2 = "0.10") - Cross-impl fixture suite: emit script generates deterministic .uniqent bundles, extracted files, expected-digest.txt, and a throwaway keypair; vitest drift-guard (3 tests) and Rust integration tests (3 tests) both assert against the same committed fixtures Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e convention The emit script wrote uniqent.json without a trailing newline; Prettier then added one, causing the on-disk files/ tree to diverge from the in-memory bundle used to compute expected-digest.txt. Rust core_test read from disk and got a different digest, breaking 2/3 tests. Fix: add '\n' to the JSON.stringify result in the emit script so the in-memory bundle, the written files/, and expected-digest.txt all agree byte-for-byte. Regenerated fixture.uniqent and expected-digest.txt accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After fixing the uniqent.json trailing-newline issue, fixture-signed.uniqent must be regenerated so it packs the same 353-byte uniqent.json that expected-digest.txt and the Rust tests now expect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port packages/core/src/secret-scan.ts to Rust (secret_scan.rs): all 5 PREFIX_PATTERNS (openai/github-pat/slack/aws-access-key/private-key), high-entropy detector (Shannon >= 4.0 + longest unbroken run >= 20), PLACEHOLDER_RE, BINARY_EXT skip, ALLOWLISTED_KEYS (pubkey/publicKey), signature.json skip, JSON/JSONL AST walk with malformed-JSON fallback. pack_checked() gates on scan before archiving (validation deferred to Phase 3 — diverges from TS pack which also calls assertValid; documented in archive.rs). sign() gains scan gate at top, matching TS sign() ordering. 19 cargo tests pass (was 8); clippy -D warnings clean; 41 vitest unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Rust JSON walk was capturing only the first hit per JSON object/line via Option<_>; now collects into a Vec so every string node's finding is recorded, matching TS semantics where walkJson calls record(detect(s)) for every string encountered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract buildFixtureBundle() from emit-core-fixtures.ts so tests can call the live TS core directly; add three drift-guard tests checking digest, pack/unpack, and sign/verify semantics against committed fixtures. Guard script body with isMain check so importing the module never writes files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the hard-coded validation.ok=true stub with real regex-based checks: Slug pattern (^[a-z0-9][a-z0-9-]*$) for name, full semver 2.0.0 pattern for version. Errors carry path/code/manifest structure matching the TS spec. Add validation_test.rs with 9 cargo tests covering invalid name, invalid version, both-invalid, and the happy-path default session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mory/profile) Adds applyContentScript() export to emit-fixtures.ts with the canonical 8-step content mutation script (MCP/skill/channel/task/memory/profile), emits fixtures/state-content.json, and adds a Vitest drift guard. Prettier-reformats existing fixture JSON files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Extend emit-fixtures.ts to emit catalog-data.json (mcp/skills/channels from @uniqent/builder) - Add drift test in fixtures.test.ts to detect catalog-data.json divergence - Add summarizer entry to SKILL_CATALOG in packages/builder/src/catalog/skills.ts - Replace hardcoded mcp_catalog_entry/channel_catalog_entry match arms in session.rs with OnceLock<Value> lazy-parsed from catalog-data.json via include_str! - Implement Session::add_skill_catalog(name) mirroring TS Brain.addSkillFromCatalog - Add Rust unit tests: add_skill_catalog_known_name_adds_skill, add_skill_catalog_unknown_name_errors - Strengthen readd_mcp_deduplicates to also assert sorted position in state Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ected data Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port TS normalizeMcpConfig (176 lines) to Rust core with full fixture pipeline: emit-port-fixtures.ts → normalize-cases.json (12 cases), drift-guard in port-fixtures.test.ts, Rust tests in ports_test.rs. Session wired: paste_mcp_preview / add_pasted_mcp / import_mcp_servers. cargo test + clippy -D warnings all green; pnpm test 48/48 pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Extend emit-port-fixtures.ts → fixtures/ports/memory-cases.json (11 cases: headings+bullets, kind prefixes, [[entity]] links, #tags, blank, mixed, dedup, numbered list, alias wikilinks, callout prefix) - Extend test/port-fixtures.test.ts with memory-cases drift guard - Add src/ports/memory.rs: parse_memory_markdown + memory_graph (faithful port of packages/builder/src/memory/parse.ts, all parse branches: entities, tags, kinds, bullet/numbered stripping, heading skip, label truncation) - Add session: import_memory (items/markdown/text per TS /api/memory/import), preview_memory (items + graph, no mutation), memory_graph (from facts) - Extend tests/ports_test.rs: fixture-mapped tests + 7 session unit tests Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Register 18 Tauri commands (mcp/skill/channel/task/memory/profile families) and swap api.ts from soon() to invoke() for all Phase 3a content methods. Hubs, publish, vault, and install remain stubbed for Phase 3b/4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n tree stays clean) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 of rebuilding Studio as a native app (umbrella spec:
docs/superpowers/specs/2026-07-04-studio-native-design.md).What
apps/studio/src-taurihosting the existing SPA — a real macOS Studio.app, 8.8MB (no Node/Electron runtime).state/catalog/set_meta/set_targets/set_persona/set_readme/set_avatar/remove_avatar/reset).scripts/emit-fixtures.tsemitsfixtures/*.jsonfrom the real TSStudioSession; a Vitest drift guard pins the TS side andcargo testasserts the Rust session JSON-equals the same fixtures (including empty-string clear semantics, pinned bystate-cleared.json).web/src/api.tsroutes the 9 methods toinvoke()under Tauri; all 34 other methods reject with a friendly "coming in a later phase" message; browser fetch paths byte-identical (legacy server untouched until Phase 4).studio-rustmacOS job runscargo test+cargo clippy -D warnings, so TS↔Rust divergence fails the build.Gates
6/6 cargo · 38/38 vitest · typecheck · clippy — all green. Human smoke test: launch via
pnpm --filter @uniqent/studio native:dev.Out of scope (later phases)
Rust core engine (pack/sign/digest → real export) — Phase 2; builder features — Phase 3; adapters/install, signing/notarization, deleting the Node server — Phase 4.
🤖 Generated with Claude Code