Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b483266
Seed the workshop crates cleanup plan
vinniefalco Sep 24, 2026
13b3b2a
Delete the workshop's human docs and drop them from the guide build
vinniefalco Sep 24, 2026
8a17ee0
Fail the symlink tests under CI and cover the jail's edge cases
vinniefalco Sep 24, 2026
6b23dd7
Replace fixed sleeps in workshop tests with event-driven waits
vinniefalco Sep 24, 2026
62d14c9
Add unit tests for the realtime relay's refusals
vinniefalco Sep 24, 2026
82c9b74
Pin the /ws frames in a shared fixture
vinniefalco Sep 24, 2026
5dc6a03
Refuse non-loopback addresses in reuse_bind
vinniefalco Sep 24, 2026
f6903e2
Answer the route deadline with a JSON error envelope
vinniefalco Sep 24, 2026
222247b
Render route timeouts as readable errors in the UIs
vinniefalco Sep 25, 2026
6d44740
Recover from a timed-out save through unknown token state
vinniefalco Sep 25, 2026
3a40b0d
Delete WorkshopObserver, StatusBus helpers, and re-exports
vinniefalco Sep 25, 2026
1ad965d
Give the gateway app its own copies of the icons
vinniefalco Sep 25, 2026
dc587d9
Move the desktop app to crates/workshop/desktop
vinniefalco Sep 25, 2026
dc24c93
Retire shell in the Rust crates and workflows
vinniefalco Sep 25, 2026
66d53f4
Retire shell in the shared status bar and the workshop UI
vinniefalco Sep 25, 2026
3194a17
Move config-ui's views to pages
vinniefalco Sep 25, 2026
c1cbd18
Rename config-ui's views to pages and its shell to desk
vinniefalco Sep 25, 2026
fca0c88
Retire shell in the workshop docs and record the vocabulary
vinniefalco Sep 25, 2026
8f9600a
Move app.rs's children into an app directory
vinniefalco Sep 25, 2026
1aab571
Break compose into per-subsystem register helpers
vinniefalco Sep 25, 2026
3b2a42c
Split agent socket's framing helpers into their own module
vinniefalco Sep 25, 2026
1aa73bd
Split the desktop supervisor along its seams
vinniefalco Sep 25, 2026
e9c13a4
Name the phases of the heartbeat and progress run loops
vinniefalco Sep 25, 2026
79b7109
Serve the prompts contract route from the server
vinniefalco Sep 25, 2026
e0e0c6f
Share error rendering and state-bucket validator in support
vinniefalco Sep 25, 2026
a4952fa
Share the mock server test helper through workshop-support
vinniefalco Sep 25, 2026
63ba274
Move workspace and gateway progress groups into directories
vinniefalco Sep 25, 2026
f932a9b
Remove the server's pre-decomposition module aliases
vinniefalco Sep 25, 2026
4c0f465
Move the /ws socket into a workshop_socket module
vinniefalco Sep 25, 2026
5a67ec1
Rename the status relay, SwitchOutcome, and socket wording
vinniefalco Sep 25, 2026
1480745
Return named registration structs from every subsystem
vinniefalco Sep 25, 2026
c7eeec8
Move workshop UI's backoff and service tokens into services
vinniefalco Sep 25, 2026
2c0cb1e
Correct the workshop's code-level docs
vinniefalco Sep 25, 2026
4aae1e3
Record the exit gate results
vinniefalco Sep 25, 2026
ce10a8e
Close plan: workshop crates cleanup
vinniefalco Sep 25, 2026
a745809
Add temperature and max_tokens options to models.use
vinniefalco Sep 24, 2026
3b89be6
Gate tray icon tints to Windows and Linux
vinniefalco Sep 24, 2026
7acf5cd
Bind no-thinking to switchable models; idle model timeout
vinniefalco Sep 24, 2026
a6b9a74
Close plan: issues 69 59 70
vinniefalco Sep 24, 2026
704c494
Gate the jail tests' glob import to Windows
vinniefalco Sep 25, 2026
49d9799
Share one write path between editor save and Overwrite
vinniefalco Sep 25, 2026
63ddccb
Use a real-time quiet window in the heartbeat retry test
vinniefalco Sep 25, 2026
eca41e5
Correct workshop-server module docs and gate them in CI
vinniefalco Sep 25, 2026
890ae2f
Close plan: vibe2 debt removal
vinniefalco Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions .cursor/rules/workshop-architecture.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ alwaysApply: false

## One-way dependency graph

- Dependencies flow one way: shell -> features -> services -> vocabulary. Never add a dependency from a lower tier to a higher one. If Cargo rejects a cycle, the design is wrong, not the graph.
- Tiers: vocabulary (`workshop-protocol`, `workshop-support`, `workshop-registry`) <- services (`workshop-gateway`, `workshop-status`, `workshop-menu`) <- features (`workshop-sessions`, `workshop-workspace`) <- shell (`workshop-server`).
- Dependencies flow one way: server -> features -> services -> vocabulary. Never add a dependency from a lower tier to a higher one. If Cargo rejects a cycle, the design is wrong, not the graph.
- Tiers: vocabulary (`workshop-protocol`, `workshop-support`, `workshop-registry`) <- services (`workshop-gateway`, `workshop-status`, `workshop-menu`) <- features (`workshop-sessions`, `workshop-workspace`) <- server (`workshop-server`).
- Crates in the same tier never depend on each other. They meet through `workshop-protocol` wire types and `workshop-registry` proxy slots.
- Every `workshop-*` crate's `lib.rs` opens with a `//!` doc carrying a `## Invariants` marker that lists what the crate may depend on and what it may not. Read it before adding an import.
- `lib.rs` is a facade only: crate docs, crate-level attributes, `mod` declarations, and `pub use` re-exports. No logic.

## Registry pattern

- Subsystems self-register into `workshop-registry` proxy slots: routes, state handles, background tasks, push channels, and shutdown handles. The shell composes the registry; it does not wire subsystems by name.
- Subsystems self-register into `workshop-registry` proxy slots: routes, state handles, background tasks, push channels, and shutdown handles. The server composes the registry; it does not wire subsystems by name.
- Unregistered slots are graceful no-ops. Never add a hard dependency on an optional subsystem.
- Registration handles are `#[must_use]`; registry traits are sealed so only workshop crates implement them.

## File ceiling

- No file exceeds 500 lines. If an edit would push a file past 500, split first, then edit. `cargo test -p build-xtask` enforces the ceiling, the tier graph, and lint inheritance over the Rust files in the workshop crates carrying the marker; the Tauri shell (the `workshop` crate) is exempt until the headless agent mode plan.
- No file exceeds 500 lines. If an edit would push a file past 500, split first, then edit. `cargo test -p build-xtask` enforces the ceiling, the tier graph, and lint inheritance over the Rust files in the workshop crates carrying the marker; the desktop app (the `workshop` crate) is exempt until the headless agent mode plan.
2 changes: 1 addition & 1 deletion .cursor/rules/workshop-spa.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ alwaysApply: false
- The package at `crates/workshop/ui/` is a sibling of the `crates/workshop/server/` crate that builds and serves it. `src/` has three layers: `base/` (lifecycle, events, paths, the `WorkshopPart` base class), `services/` (DOM-free registries and services), and `parts/` (feature directories; every panel extends `base/workshop-part.ts`).
- Feature-based directories under `parts/` (`parts/agent/`, `parts/editor/`, `parts/layout/`, `parts/menu/`, `parts/take/`, `parts/stt/`, `parts/chrome/`, `parts/status/`, `parts/workspace/`, `parts/gateway/`). Shared code lives in `services/` or `base/`; shared UI assets (boot-loaded icons) live in `parts/shared/`; design tokens live in `tokens/`.
- Barrel exports: every directory has an `index.ts`. Lazy directories export `register()` installing commands, menu items, panel factories, and socket subscriptions.
- The boot shell (`main.ts`, `services/`, `base/`) loads immediately. Feature directories load via dynamic `import()` on first activation. Lazy-loaded panels never import the boot shell.
- The entry bundle (`main.ts`, `services/`, `base/`) loads immediately. Feature directories load via dynamic `import()` on first activation. Lazy-loaded panels never import the entry bundle.
- Registration, not central wiring: panel types, menu items, services, socket handlers, keyboard shortcuts, and lifecycle disposal self-register through the panel, menu/command, and service registries.

## CSS colocation and tokens
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ jobs:
- name: Stage Gateway sidecar
run: node tools/stage-gateway-sidecar.mjs stage --target x86_64-pc-windows-msvc --source target/debug/promptforge-gateway.exe

# workshop-server-api is the shell's re-export view of workshop-server,
# workshop-server-api is the desktop app's re-export view of workshop-server,
# so it runs in this partition with the crates it fronts and stays
# out of the workspace-wide jobs like them.
- name: Clippy (workshop)
Expand All @@ -199,6 +199,11 @@ jobs:
- name: Doctests (workshop)
run: cargo test --doc -p workshop -p workshop-server -p workshop-server-api

- name: Docs (workshop-server, private items)
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --locked --no-deps -p workshop-server --document-private-items

- name: Test Gateway process ownership races
shell: bash
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,13 @@ jobs:
src="target/release/promptforge-gateway$ext"
;;
esac
mkdir -p crates/workshop/shell/binaries
cp "$src" "crates/workshop/shell/binaries/promptforge-gateway-$triple$ext"
mkdir -p crates/workshop/desktop/binaries
cp "$src" "crates/workshop/desktop/binaries/promptforge-gateway-$triple$ext"

- name: Build the app
uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # v0
with:
projectPath: crates/workshop/shell
projectPath: crates/workshop/desktop
args: ${{ matrix.args }} --config tauri.nightly.conf.json

- name: Upload the installers
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/release-workshop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ jobs:
src="target/release/promptforge-gateway$ext"
;;
esac
mkdir -p crates/workshop/shell/binaries
staged="crates/workshop/shell/binaries/promptforge-gateway-$triple$ext"
mkdir -p crates/workshop/desktop/binaries
staged="crates/workshop/desktop/binaries/promptforge-gateway-$triple$ext"
cp "$src" "$staged"
output=$("$staged" --version)
version="${{ needs.prepare.outputs.version }}"
Expand All @@ -180,15 +180,15 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
projectPath: crates/workshop/shell
projectPath: crates/workshop/desktop
args: ${{ matrix.args }}

- name: Build the unsigned temporary app
if: github.event_name == 'workflow_dispatch'
id: temporary-build
uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # v0
with:
projectPath: crates/workshop/shell
projectPath: crates/workshop/desktop
args: ${{ matrix.args }} --config tauri.nightly.conf.json

- name: The signed build has the workspace version
Expand Down Expand Up @@ -292,7 +292,7 @@ jobs:
Select-Object -First 1
if (-not $exe) { throw "promptforge-workshop.exe not found after install" }
# The gateway ships as an externalBin sidecar; a default (all
# components checked) silent install must lay it beside the shell.
# components checked) silent install must lay it beside the desktop app.
$gateway = @("$env:LOCALAPPDATA", "$env:ProgramFiles", "${env:ProgramFiles(x86)}") |
ForEach-Object { Get-ChildItem $_ -Recurse -Filter promptforge-gateway.exe -ErrorAction SilentlyContinue } |
Select-Object -First 1
Expand All @@ -304,7 +304,7 @@ jobs:
$output
$configDir = Join-Path $env:USERPROFILE ".promptforge"
New-Item -ItemType Directory -Path $configDir -Force | Out-Null
# A default install lays the gateway beside the shell, so boot
# A default install lays the gateway beside the desktop app, so boot
# launches it instead of using an explicit endpoint. A minimal
# gateway.toml keeps that first boot free of STT model downloads;
# the dummy workshop.toml endpoint remains the fallback when no
Expand Down Expand Up @@ -360,15 +360,15 @@ jobs:
cp -R "/Volumes/PromptForge/PromptForge.app" /tmp/PromptForge.app
hdiutil detach "/Volumes/PromptForge" -quiet
# The bundle holds two executables (the gateway ships via
# externalBin beside the main binary); pick the shell by name.
# externalBin beside the main binary); pick the desktop app by name.
bin="/tmp/PromptForge.app/Contents/MacOS/promptforge-workshop"
[ -x "$bin" ] || { echo "promptforge-workshop not in the bundle"; exit 1; }
version="${{ needs.prepare.outputs.version }}"
output=$("$bin" --version)
expected="promptforge-workshop $version"
[ "$output" = "$expected" ] || { echo "--version printed '$output', expected '$expected'"; exit 1; }
mkdir -p "$HOME/.promptforge"
# A default install lays the gateway beside the shell, so boot
# A default install lays the gateway beside the desktop app, so boot
# launches it instead of using an explicit endpoint. A minimal
# gateway.toml keeps that first boot free of STT model downloads;
# the dummy workshop.toml endpoint remains the fallback when no
Expand Down Expand Up @@ -407,7 +407,7 @@ jobs:
package=$(dpkg-deb -f "$deb" Package)
# The deb holds two executables (the gateway ships via
# externalBin into /usr/bin beside the main binary); pick the
# shell by name.
# desktop app by name.
bin=$(dpkg -L "$package" | grep -E '/usr/bin/promptforge-workshop$' | head -1)
[ -n "$bin" ] || { echo "no promptforge-workshop in the deb"; exit 1; }
version="${{ needs.prepare.outputs.version }}"
Expand All @@ -421,7 +421,7 @@ jobs:
output=$("$appimage" --version)
[ "$output" = "$expected" ] || { echo "AppImage --version printed '$output', expected '$expected'"; exit 1; }
mkdir -p "$HOME/.promptforge"
# A default install lays the gateway beside the shell, so boot
# A default install lays the gateway beside the desktop app, so boot
# launches it instead of using an explicit endpoint. A minimal
# gateway.toml keeps that first boot free of STT model downloads;
# the dummy workshop.toml endpoint remains the fallback when no
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/workshop-installer-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
paths:
- .github/workflows/workshop-installer-smoke.yml
- crates/build-workshop/**
- crates/workshop/shell/installer.nsi
- crates/workshop/shell/tauri*.conf.json
- crates/workshop/desktop/installer.nsi
- crates/workshop/desktop/tauri*.conf.json
- tools/stage-gateway-sidecar.mjs
workflow_dispatch:

Expand Down Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Compile unsigned debug NSIS installer
uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # v0
with:
projectPath: crates/workshop/shell
projectPath: crates/workshop/desktop
args: --debug --bundles nsis --config tauri.nightly.conf.json

- name: Remove Gateway sidecar
Expand Down
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
/crates/workshop/ui/dist/
/crates/gateway/config-ui/ui/dist/
# tauri-build's generated ACL schemas, regenerated on every workshop build.
/crates/workshop/shell/gen/
/crates/workshop/desktop/gen/
# The gateway sidecar staged for bundle.externalBin by CI before
# `tauri build` (crates/workshop/shell/tauri.conf.json); a build artifact.
/crates/workshop/shell/binaries/
# `tauri build` (crates/workshop/desktop/tauri.conf.json); a build artifact.
/crates/workshop/desktop/binaries/
/plan-dist-manifest.json
Loading
Loading