Skip to content

Set up Tauri + Svelte project structure - #11

Merged
malibio merged 1 commit into
mainfrom
feature/issue-1-tauri-svelte-setup
Aug 4, 2025
Merged

malibio merged 1 commit into
mainfrom
feature/issue-1-tauri-svelte-setup

Conversation

@malibio

@malibio malibio commented Aug 4, 2025

Copy link
Copy Markdown
Collaborator

Closes #1

Summary

This PR implements the foundational Tauri + Svelte project structure for NodeSpace, providing a working desktop application with all required placeholder panels.

Implementation Details

  • Cargo Workspace: Set up root workspace configuration with nodespace-app member
  • Tauri Configuration: Configured NodeSpace desktop app with proper branding, window settings (1200x800, min 800x600), and resizable windows
  • Svelte Frontend: Created modern UI with placeholder panels for all core views:
    • JournalView (hierarchical note organization)
    • LibraryView (knowledge base and documents)
    • NodeViewer (main content editing area)
    • AIChatView (AI assistant interaction)
  • Integration: Tauri-Svelte communication with version display and connection testing
  • Build System: Verified development server, production build, and dependency management

Acceptance Criteria Verification

  • ✅ Tauri desktop app launches successfully
  • ✅ Svelte frontend renders with placeholder panels (JournalView, LibraryView, AIChatView, NodeViewer)
  • ✅ Development server works with hot reload (npm run tauri:dev)
  • ✅ Build process creates distributable package (npm run tauri:build)
  • ✅ No compilation errors or warnings
  • ✅ App window is resizable with minimum dimensions enforced
  • ✅ App shows version information from Tauri
  • ✅ All dependencies install correctly

Testing Commands

All verification commands pass successfully:

npm install && cd nodespace-app && npm install
cargo check --workspace
npm run build
npm run tauri:dev
npm run tauri:build

This foundation enables development of Issues #2-#5 and provides the base desktop application for NodeSpace features.

🤖 Generated with Claude Code

- Set up Cargo workspace with nodespace-app Tauri application
- Created Svelte frontend with placeholder UI panels
- Configured NodeSpace desktop app with proper branding and window settings
- Implemented multi-panel layout: JournalView, LibraryView, NodeViewer, AIChatView
- Added Tauri-Svelte integration with version display and connection testing
- Verified all acceptance criteria: dev server, build process, no errors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@malibio
malibio merged commit c8e62e0 into main Aug 4, 2025
@malibio
malibio deleted the feature/issue-1-tauri-svelte-setup branch August 4, 2025 22:51
malibio added a commit that referenced this pull request Feb 4, 2026
- Set up Cargo workspace with nodespace-app Tauri application
- Created Svelte frontend with placeholder UI panels
- Configured NodeSpace desktop app with proper branding and window settings
- Implemented multi-panel layout: JournalView, LibraryView, NodeViewer, AIChatView
- Added Tauri-Svelte integration with version display and connection testing
- Verified all acceptance criteria: dev server, build process, no errors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
malibio added a commit that referenced this pull request Feb 26, 2026
- Set up Cargo workspace with nodespace-app Tauri application
- Created Svelte frontend with placeholder UI panels
- Configured NodeSpace desktop app with proper branding and window settings
- Implemented multi-panel layout: JournalView, LibraryView, NodeViewer, AIChatView
- Added Tauri-Svelte integration with version display and connection testing
- Verified all acceptance criteria: dev server, build process, no errors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
malibio pushed a commit that referenced this pull request Jun 8, 2026
…d code

- Fix Ollama double-prefix: ollamaValue() now returns the raw daemon ID
  ("ollama:<name>") directly instead of prepending another "ollama:" prefix
- Fix openai-compat encoding: persist UUID (configId) as modelId in both
  selector and node, not the display name; decodeSelection now prefix-matches
  instead of splitting on every colon (handles colons in Ollama model names)
- Fix selectorCurrentValue: Ollama case passes raw daemon ID through unchanged
- Add model-load overlay in AiChatNodeViewer: spinner shown while
  ensureModelReady is running, covering the download case (AC #11)
- Disable openai-compat options with "(coming soon)" label until send path
  is wired to the daemon
- Delete ai-chat-model-picker.svelte (dead code, no longer imported)
- Fix stale AiChatModelPicker reference in model-store comment

Co-Authored-By: Claude <noreply@anthropic.com>
malibio added a commit that referenced this pull request Jun 8, 2026
…atible as open paths (#1371)

* Unified model picker + Settings AI Models revamp (closes #1370)

- New AiChatModelSelector dropdown replaces two-step provider→model flow:
  Local/Ollama/OpenAI-compat sections, greyed-out headers, "Set up..." action
- Model selector locks (disabled) after first user message is sent
- <16 GB RAM: Local section greyed out with explanation
- Ollama auto-detected at localhost:11434; live model list; "not running" state
- OpenAI-compatible named configs: add/edit/remove in Settings → AI Models
- Default model setting persisted to localStorage, applied to new chats
- Settings → AI Models: Local (Ministral 8B only), Ollama status, OpenAI-compat CRUD
- Rust: recommended_model_id() returns ministral-8b-q4km; chat_model_list filters
  GGUF catalog to ministral-* only at the Tauri command boundary
- AiChatProvider type extended with openai-compat variant

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

* Address review: fix encoding bugs, add model-load overlay, remove dead code

- Fix Ollama double-prefix: ollamaValue() now returns the raw daemon ID
  ("ollama:<name>") directly instead of prepending another "ollama:" prefix
- Fix openai-compat encoding: persist UUID (configId) as modelId in both
  selector and node, not the display name; decodeSelection now prefix-matches
  instead of splitting on every colon (handles colons in Ollama model names)
- Fix selectorCurrentValue: Ollama case passes raw daemon ID through unchanged
- Add model-load overlay in AiChatNodeViewer: spinner shown while
  ensureModelReady is running, covering the download case (AC #11)
- Disable openai-compat options with "(coming soon)" label until send path
  is wired to the daemon
- Delete ai-chat-model-picker.svelte (dead code, no longer imported)
- Fix stale AiChatModelPicker reference in model-store comment

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

---------

Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local>
Co-authored-by: Claude <noreply@anthropic.com>
malibio added a commit that referenced this pull request Jun 22, 2026
…atible as open paths (#1371)

* Unified model picker + Settings AI Models revamp (closes #1370)

- New AiChatModelSelector dropdown replaces two-step provider→model flow:
  Local/Ollama/OpenAI-compat sections, greyed-out headers, "Set up..." action
- Model selector locks (disabled) after first user message is sent
- <16 GB RAM: Local section greyed out with explanation
- Ollama auto-detected at localhost:11434; live model list; "not running" state
- OpenAI-compatible named configs: add/edit/remove in Settings → AI Models
- Default model setting persisted to localStorage, applied to new chats
- Settings → AI Models: Local (Ministral 8B only), Ollama status, OpenAI-compat CRUD
- Rust: recommended_model_id() returns ministral-8b-q4km; chat_model_list filters
  GGUF catalog to ministral-* only at the Tauri command boundary
- AiChatProvider type extended with openai-compat variant

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

* Address review: fix encoding bugs, add model-load overlay, remove dead code

- Fix Ollama double-prefix: ollamaValue() now returns the raw daemon ID
  ("ollama:<name>") directly instead of prepending another "ollama:" prefix
- Fix openai-compat encoding: persist UUID (configId) as modelId in both
  selector and node, not the display name; decodeSelection now prefix-matches
  instead of splitting on every colon (handles colons in Ollama model names)
- Fix selectorCurrentValue: Ollama case passes raw daemon ID through unchanged
- Add model-load overlay in AiChatNodeViewer: spinner shown while
  ensureModelReady is running, covering the download case (AC #11)
- Disable openai-compat options with "(coming soon)" label until send path
  is wired to the daemon
- Delete ai-chat-model-picker.svelte (dead code, no longer imported)
- Fix stale AiChatModelPicker reference in model-store comment

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

---------

Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local>
Co-authored-by: Claude <noreply@anthropic.com>
malibio added a commit that referenced this pull request Jul 28, 2026
…n daemon death

Reworks the WIP checkpoint into its final form and addresses the remaining
review findings from PR #1778.

🔴 #1 — mid-run daemon death scored noTools scenarios as passes. Preflight
proved the environment good at t=0 but not that it stayed good, and a run takes
8+ minutes. A daemon dying mid-group left every subsequent turn calling no
tools, so negative assertions went green — the exact false result this harness
exists to prevent, relocated past the gate. TurnRecord now carries `sendFailed`
(a turn that never reached the model is not the same as a turn that called no
tools), and two consecutive failed sends abort the run as an environment error.
Verified against a live daemon killed mid-group: exit 2, 10 scored scenarios
discarded, no results file. The same case previously reported "9/12 scenarios
failed" with exit 1.

🟡 #2 — readServedDatabasePath failed open. Every other check failing open costs
a missed detection; this one guards against writes into live user data that
cannot be undone, so "I could not determine which database this is" must stop
the run. Now throws on non-zero exit, unparseable JSON, or an empty database
list. Verified the real-database refusal still wins over the model check.

🟡 #3 — substring model matching accepted a different quantization
(NS_MODEL=gemma-4-e4b would match a loaded gemma-4-e4b-q8, whose tool-calling
behavior differs). Now exact-match first, with a filename fallback for the case
where the daemon reports a resolved GGUF path instead of a catalog id, and a
warning when only the fallback matched.

🟡 #5 — get_status collapsed an engine error into "no model loaded". Added a
tracing::warn! on the error arm; the degrade-don't-fail behavior is unchanged.

🟢 #6 stale doc path (agent-evals.md → agent-eval.md).
🟢 #9 the contamination guard now selects fixtures by whether they declare
   scenario prompts, so a shared helper dropped in fixtures/ is skipped rather
   than failing the build with a misleading parser-drift message. Re-verified
   the guard still fires on a planted example and is not vacuous.
🟢 #11 CLAUDE.md records that tests under scripts/ must stay DOM-free, so the
   test:scripts exception cannot quietly widen.
🟢 #12 documented that env.log/env.timeoutMs exist to state the contract
   scripts/aichat.ts consumes from the environment directly.
🟢 #13 usage errors now exit 64 rather than sharing 1 with scenario failures.

Also corrected the abort footer, which claimed "no scenarios were run" when a
mid-run abort had already scored some.

Deferred, with reasons:
- 🟡 #4 (get_status can block a tokio worker for the length of a generation)
  is real but latent — nothing polls that RPC today. The fix is a daemon-internal
  refactor beyond this PR's scope; filed as #1779.
- 🟢 #7 (default --baseline path) depends on #1764 recording the honest baseline
  first; wiring it now would default to a file that does not exist.
- 🟢 #8 (generic EvalFixture<S>) is the right shape but a refactor of a
  brand-new abstraction with two consumers — the reviewer's own YAGNI call.
- 🟢 #10 (enforce the 6600-token constant matches N_CTX_MINIMUM) — both sites
  carry cross-referencing comments and the value is a floor, not an exact
  accounting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFMbS5rEGLQL6CCkg6Xjs
malibio added a commit that referenced this pull request Jul 28, 2026
…n daemon death

Reworks the WIP checkpoint into its final form and addresses the remaining
review findings from PR #1778.

🔴 #1 — mid-run daemon death scored noTools scenarios as passes. Preflight
proved the environment good at t=0 but not that it stayed good, and a run takes
8+ minutes. A daemon dying mid-group left every subsequent turn calling no
tools, so negative assertions went green — the exact false result this harness
exists to prevent, relocated past the gate. TurnRecord now carries `sendFailed`
(a turn that never reached the model is not the same as a turn that called no
tools), and two consecutive failed sends abort the run as an environment error.
Verified against a live daemon killed mid-group: exit 2, 10 scored scenarios
discarded, no results file. The same case previously reported "9/12 scenarios
failed" with exit 1.

🟡 #2 — readServedDatabasePath failed open. Every other check failing open costs
a missed detection; this one guards against writes into live user data that
cannot be undone, so "I could not determine which database this is" must stop
the run. Now throws on non-zero exit, unparseable JSON, or an empty database
list. Verified the real-database refusal still wins over the model check.

🟡 #3 — substring model matching accepted a different quantization
(NS_MODEL=gemma-4-e4b would match a loaded gemma-4-e4b-q8, whose tool-calling
behavior differs). Now exact-match first, with a filename fallback for the case
where the daemon reports a resolved GGUF path instead of a catalog id, and a
warning when only the fallback matched.

🟡 #5 — get_status collapsed an engine error into "no model loaded". Added a
tracing::warn! on the error arm; the degrade-don't-fail behavior is unchanged.

🟢 #6 stale doc path (agent-evals.md → agent-eval.md).
🟢 #9 the contamination guard now selects fixtures by whether they declare
   scenario prompts, so a shared helper dropped in fixtures/ is skipped rather
   than failing the build with a misleading parser-drift message. Re-verified
   the guard still fires on a planted example and is not vacuous.
🟢 #11 CLAUDE.md records that tests under scripts/ must stay DOM-free, so the
   test:scripts exception cannot quietly widen.
🟢 #12 documented that env.log/env.timeoutMs exist to state the contract
   scripts/aichat.ts consumes from the environment directly.
🟢 #13 usage errors now exit 64 rather than sharing 1 with scenario failures.

Also corrected the abort footer, which claimed "no scenarios were run" when a
mid-run abort had already scored some.

Deferred, with reasons:
- 🟡 #4 (get_status can block a tokio worker for the length of a generation)
  is real but latent — nothing polls that RPC today. The fix is a daemon-internal
  refactor beyond this PR's scope; filed as #1779.
- 🟢 #7 (default --baseline path) depends on #1764 recording the honest baseline
  first; wiring it now would default to a file that does not exist.
- 🟢 #8 (generic EvalFixture<S>) is the right shape but a refactor of a
  brand-new abstraction with two consumers — the reviewer's own YAGNI call.
- 🟢 #10 (enforce the 6600-token constant matches N_CTX_MINIMUM) — both sites
  carry cross-referencing comments and the value is a floor, not an exact
  accounting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFMbS5rEGLQL6CCkg6Xjs
malibio added a commit that referenced this pull request Jul 28, 2026
… the environment is broken, and make a new eval a fixture instead of a copied harness (#1778)

* Shared eval runner with a preflight gate (closes #1759)

Both agent evals independently implemented the same five concerns — the NS_*
environment contract, argv parsing, results assembly, the summary table, and
baseline comparison — and neither could tell a real result from a broken
environment.

Shared runner (scripts/eval/):
- runner.ts owns env, preflight, argv, chat-node lifecycle, results, summary,
  baseline diff, and exit codes. An eval is now a fixture module exporting
  scenarios plus a score function; adding one is a file in fixtures/, not
  another copy of the plumbing.
- Both existing evals port over with their scoring logic moved verbatim, so
  scenario behavior is unchanged. Scenarios gained stable `id`s because
  baseline diffing joins on them — prompts get reworded (decontamination
  rewrote every one) and joining on text reads as remove-and-add.

Preflight gate — the load-bearing part. Before scoring anything it asserts the
environment can produce a valid result, exits 2 (distinct from a scenario
failure), and writes no results file:
- daemon reachable
- NOT serving the real user database
- the requested model is loaded, and is the one being scored
- the granted context window exceeds the agent's system prompt

The context check is what the issue was written about: a run once scored its two
"no tools called" scenarios as PASSING while every turn died on context overflow
before inference — a failed turn calls no tools, so a negative assertion goes
green. It catches that in ~2s instead of ~8min of half-broken inference.

The database check was added after this branch's own eval runs wrote schemas and
chat nodes into ~/.nodespace. NODESPACED_DB_PATH — what the old reproduce steps
prescribed — is overridden by the database registry (ADR-053), so the daemon
serves live user data while looking correctly configured. NODESPACE_HOME is the
variable that actually isolates; the gate now refuses to run without it.

Granted n_ctx over the wire:
- LocalAgentStatusResponse gains model_id and granted_n_ctx; get_status reports
  the catalog id the model was loaded by (not the resolved GGUF path, which no
  requested-id substring matches) and the window the engine actually allocated.
- New `nodespace model status [--json]` surfaces it. Previously this value
  existed only in-process and had to be scraped out of daemon log text.

Provenance in every results file — model, timestamp, host RAM, granted n_ctx,
commit, and dirty flag — so a cited number carries the conditions that produced
it. No blessed baseline is committed: the evals are nondeterministic and
host-specific, #1764 owns recording the honest one, and #1775's fixes will move
it. Comparison is first-class via --baseline instead.

Contamination guard now enumerates scripts/eval/fixtures/ rather than naming two
files, so an eval added later is guarded automatically. Verified it still fires
on a planted example from the new location and is not vacuous.

test:scripts runs the scoring unit test (21 tests, ~20ms, no model or daemon) and
joins test:all. It ran nowhere before. This is a deliberate exception to the
"never use bun test" rule, which exists to stop DOM tests bypassing the
Happy-DOM vitest config — not applicable to a standalone script test.

Eval setup moved to nodespace-docs/development/agent-eval.md and
scripts/aichat-1329-trial.md deleted, per the docs standard.

The evals themselves stay out of test:all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFMbS5rEGLQL6CCkg6Xjs

* WIP: address review findings 1 and 2 (checkpoint before /address-review)

Critical #1: mark failed sends with sendFailed and abort the run after two
consecutive failures, so a daemon that dies mid-run cannot score noTools
assertions as passes.

Important #2: readServedDatabasePath now fails CLOSED — an undeterminable
database path stops the run instead of skipping the check that guards live
user data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Address review: fail closed on the destructive check, abort on mid-run daemon death

Reworks the WIP checkpoint into its final form and addresses the remaining
review findings from PR #1778.

🔴 #1 — mid-run daemon death scored noTools scenarios as passes. Preflight
proved the environment good at t=0 but not that it stayed good, and a run takes
8+ minutes. A daemon dying mid-group left every subsequent turn calling no
tools, so negative assertions went green — the exact false result this harness
exists to prevent, relocated past the gate. TurnRecord now carries `sendFailed`
(a turn that never reached the model is not the same as a turn that called no
tools), and two consecutive failed sends abort the run as an environment error.
Verified against a live daemon killed mid-group: exit 2, 10 scored scenarios
discarded, no results file. The same case previously reported "9/12 scenarios
failed" with exit 1.

🟡 #2 — readServedDatabasePath failed open. Every other check failing open costs
a missed detection; this one guards against writes into live user data that
cannot be undone, so "I could not determine which database this is" must stop
the run. Now throws on non-zero exit, unparseable JSON, or an empty database
list. Verified the real-database refusal still wins over the model check.

🟡 #3 — substring model matching accepted a different quantization
(NS_MODEL=gemma-4-e4b would match a loaded gemma-4-e4b-q8, whose tool-calling
behavior differs). Now exact-match first, with a filename fallback for the case
where the daemon reports a resolved GGUF path instead of a catalog id, and a
warning when only the fallback matched.

🟡 #5 — get_status collapsed an engine error into "no model loaded". Added a
tracing::warn! on the error arm; the degrade-don't-fail behavior is unchanged.

🟢 #6 stale doc path (agent-evals.md → agent-eval.md).
🟢 #9 the contamination guard now selects fixtures by whether they declare
   scenario prompts, so a shared helper dropped in fixtures/ is skipped rather
   than failing the build with a misleading parser-drift message. Re-verified
   the guard still fires on a planted example and is not vacuous.
🟢 #11 CLAUDE.md records that tests under scripts/ must stay DOM-free, so the
   test:scripts exception cannot quietly widen.
🟢 #12 documented that env.log/env.timeoutMs exist to state the contract
   scripts/aichat.ts consumes from the environment directly.
🟢 #13 usage errors now exit 64 rather than sharing 1 with scenario failures.

Also corrected the abort footer, which claimed "no scenarios were run" when a
mid-run abort had already scored some.

Deferred, with reasons:
- 🟡 #4 (get_status can block a tokio worker for the length of a generation)
  is real but latent — nothing polls that RPC today. The fix is a daemon-internal
  refactor beyond this PR's scope; filed as #1779.
- 🟢 #7 (default --baseline path) depends on #1764 recording the honest baseline
  first; wiring it now would default to a file that does not exist.
- 🟢 #8 (generic EvalFixture<S>) is the right shape but a refactor of a
  brand-new abstraction with two consumers — the reviewer's own YAGNI call.
- 🟢 #10 (enforce the 6600-token constant matches N_CTX_MINIMUM) — both sites
  carry cross-referencing comments and the value is a floor, not an exact
  accounting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFMbS5rEGLQL6CCkg6Xjs

* Address re-review: guard prior-context turns against a mid-run daemon death

Re-review of PR #1778 approved the fix but found the consecutive-failure guard
sitting on only one of two structurally identical call sites.

🟡 A — `runTurn` is called for prior-context turns as well as the scored turn,
and only the scored turn counted against the failure budget. Two routing
fixtures use `priorTurns`, both loadBearing: instance-not-schema-invoice (the
only fixture testing existing-type vs new-type routing) and
clarification-then-fallthrough (the only fixture testing the clarification
contract, with two prior turns).

The uncaught case was a prior turn failing while the scored turn succeeded: the
counter never incremented, so the scenario ran against a chat that never
received its setup and the verdict was filed as the model's routing behavior
rather than as an environment artifact. A daemon dying during a two-prior-turn
setup was also only caught two scenarios later, after three failed sends.

Hoisted the counting into a `turn()` helper both call sites go through, so the
budget covers every send. Verified against a live daemon killed during a
prior-context turn: exit 2, 7 scored scenarios discarded, no results file.

Nits from the same review:
- `eval_prompts(&source).1.gt(&0)` → `> 0` (reads faster; same semantics).
- A fallback filename match now records `modelMatchedByPath` in the results
  file's provenance, so the "exact build unconfirmed" caveat outlives the
  terminal session that saw the stderr warning.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFMbS5rEGLQL6CCkg6Xjs

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Set up Tauri + Svelte project structure

1 participant